DSL 的隐式参数 "chaining"

Implicit parameter "chaining" for DSL

我有一个想法(模糊),以这种方式传递(或链接)一些隐式值,而不是将参数引入块 f:

def block(x: Int)(f: => Unit)(implicit v: Int) = {
  implicit val nv = v + x
  f
}

def fun(implicit v: Int) = println(v)

如果我使用类似的东西:

implicit val ii: Int = 0
block(1) {
  block(2) {
    fun
  }
}

它会打印 3.

如果我可以说def block(x: Int)(f: implicit Int => Unit)

换句话说,我正在寻找一些允许我实现此 DSL 的设计模式:访问嵌套块内的一些累积值,但没有明确地将其作为参数传递。可能吗? (implicit 不是必需的,只是一个强调我不想显式传递该累加器的提示)。当然上面的代码会打印 0.

编辑:一种可能的用法:以下列方式组成 http 路由

prefix("path") {
  prefix("subpath") {
    post("action1") { (req, res) => do action }
    get("action2") { (req, res) => do action }
  }
}

此处 postget 将访问(如何?)累积前缀,例如 List("path", "subpath")"/path/subpath/"

通过 implicit 传递状态很脏,会导致意外且难以追踪错误。您要做的是构建一个函数,该函数可以以嵌套调用在某些操作上累积的方式组合,而其他任何东西都使用该值来执行该函数?

case class StateAccum[S](init: S){
  val op: S => S
  def flatMap[A <: S](f: S => StateAccum[A]) ={
    val StateAccum(out) = f(s)
    StateAccum(op(init, out))
  }
  def apply(f: S => A) = f(init)
}

这可以让您完全按照自己的意愿行事,只需稍微改变您的称呼方式即可。

现在,如果您真的想要嵌套控制结构,您的应用程序将不得不使用隐式值来区分 return 的类型,以便将函数应用于一个 flatMapStateAccum return 秒。它变得疯狂但看起来像下面这样:

 def apply[A](f: S => A)(implicit mapper: Mapper[S, A]): mapper.Out = mapper(this, f)

 trait Mapper[S, A]{
   type Out
   def apply(s: StateAccum[S], f: S => A): Out
 }

 object Mapper extends LowPriorityMapper{
   implicit def acuum[S, A <: S] = new Mapper[S, StateAccum[A]]{
     type Out = StateAccum[A]
     def apply(s: StateAccum[S], f: S => StateAccum[A]) = s.flatMap(f)
   }
 }

 trait LowPriorityMapper{
   implicit def acuum[S, A] = new Mapper[S, A]{
     type Out = A
     def apply(s: StateAccum[S], f: S => A) = f(s.init)
   }
 }

考虑为此使用 DynamicVariable。使用起来真的很简单,而且线程安全:

val acc: DynamicVariable[Int] = new DynamicVariable(0)

def block(x: Int)(f: => Unit) = {
  acc.withValue(acc.value + x)(f)
}

def fun = println(acc.value)