如何使用隐式参数实现FRP?

时间:2016-10-08 03:48:22

标签: scala functional-programming reactive-programming frp

我正在研究Martin Odersky的Principles of Reactive Programming。在谈到简单的FRP框架的实现时,他在开始时给出了一个使用StackableVariable(即DynamicVairable)来跟踪当前更新的信号,我可以理解。但是在幻灯片的最后,他提到更清晰的解决方案是使用隐式参数而不是DynamicVariable。有谁能告诉我这是怎么做到的?

1 个答案:

答案 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