我正在研究Martin Odersky的Principles of Reactive Programming。在谈到简单的FRP框架的实现时,他在开始时给出了一个使用StackableVariable
(即DynamicVairable
)来跟踪当前更新的信号,我可以理解。但是在幻灯片的最后,他提到更清晰的解决方案是使用隐式参数而不是DynamicVariable
。有谁能告诉我这是怎么做到的?
答案 0 :(得分:3)
幻灯片的链接对我不起作用。当我尝试使用Google搜索时,我现在使用1作为参考。
动态变量是一个线程局部变量,它保存当前Signal的状态。这是必需的,以便Signal的apply方法可以访问此信息。让我们考虑以下示例代码:
val a: Signal[Int] = ???
val b: Signal[Int] = ???
val xPlusY: Signal[Int] = Signal(a() + b())
这里,当调用a()
时,它会将自己添加到当前评估信号的依赖项列表中。由于此信息在其他任何地方都无法访问,我们基本上使用线程局部的a.k.a.全局变量。
此解决方案存在一些问题。例如,如果我们不在任何a()
内,我们也可以致电Signal()
。此外,我们必须使用全局变量。
解决方案是通过隐式参数将此信息提供给a()
:我们从
Signal[T]#apply(): T
到
Signal[T]#apply()(implicit triggeredBy: Signal[_])
(请注意,我们可能想要使用一些封装信号的新类型TriggeredBy
)
这样,此方法的实现将无需全局/线程局部变量即可访问其原始信号。
但现在我们必须以某种方式提供这种隐含。一种选择是从
更改信号创建功能的签名def Signal.apply[T](fun: => T): Signal[T]
到
def Signal.apply[T](fun: Signal[_] => T): Signal[T]
不幸的是,我们的示例代码的语法必须改变,因为我们必须提供一个函数而不是一个正文:
val xPlusY: Signal[Int] = Signal { implicit triggeredBy => a() + b() }
这个问题有几个解决方案:
等到实现隐式函数类型2。这可能很快就会发生,但它允许我们按如下方式编写Signal.apply
签名:
def Signal.apply[T](fun: implicit Signal[_] => T): Signal[T]
然后可以再次编写Signal(a() + b())
。
使用一些宏魔法将Signal(a() + b())
格式的代码转换为Signal.internalApply(implicit triggeredBy => a() + b())
代码。这意味着,Signal.apply
现在是一个宏。这是scala.rx 3已经消失的道路,从使用的角度看它运作良好。这也允许我们再次写Signal(a() + b())
。
更新:更新链接到隐式函数说明更详细的博客artikle