如何使用隐式参数实现FRP?
How to implement FRP using implicit parameter?
我正在研究 Martin Odersky 的 Principles of Reactive Programming。在讲一个简单的FRP框架的实现时,他一开始就给出了一个用StackableVariable
(即DynamicVairable
)来跟踪当前更新的信号,这个我可以理解。但在幻灯片的最后,他提到一个更简洁的解决方案是使用隐式参数而不是 DynamicVariable
。谁能告诉我如何做到这一点?
幻灯片的 link 对我不起作用。当我尝试谷歌搜索时,我现在使用 1 作为参考。
动态变量是一个线程局部变量,它保存当前评估的 Signal 的状态。这是必需的,以便 Signal 的应用方法可以访问此信息。让我们考虑以下示例代码:
val a: Signal[Int] = ???
val b: Signal[Int] = ???
val xPlusY: Signal[Int] = Signal(a() + b())
这里,当 a()
被调用时,它会将自己添加到当前评估信号的依赖项列表中。由于无法在其他任何地方访问此信息,因此我们基本上使用线程本地 a.k.a。全局变量。
这个解决方案有一些问题。例如,如果我们不在任何 Signal()
中,我们也可以调用 a()
。另外,好吧,我们必须使用一个全局变量。
解决方案是通过隐式参数将此信息提供给 a()
:我们将签名从
Signal[T]#apply(): T
到
Signal[T]#apply()(implicit triggeredBy: Signal[_])
(请注意,我们可能希望使用一些封装 Signal 的新类型 TriggeredBy
)
这样,此方法的实现将无需 global/thread-local 变量即可访问其原始信号。
但是现在我们必须以某种方式提供这个隐含的。一种选择是同时更改信号创建函数的签名
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.rx3走过的路,从使用的角度来看效果很好。这样也可以让我们再写Signal(a() + b())
更新:更新link 隐式函数解释到更详细的博客文章
我正在研究 Martin Odersky 的 Principles of Reactive Programming。在讲一个简单的FRP框架的实现时,他一开始就给出了一个用StackableVariable
(即DynamicVairable
)来跟踪当前更新的信号,这个我可以理解。但在幻灯片的最后,他提到一个更简洁的解决方案是使用隐式参数而不是 DynamicVariable
。谁能告诉我如何做到这一点?
幻灯片的 link 对我不起作用。当我尝试谷歌搜索时,我现在使用 1 作为参考。
动态变量是一个线程局部变量,它保存当前评估的 Signal 的状态。这是必需的,以便 Signal 的应用方法可以访问此信息。让我们考虑以下示例代码:
val a: Signal[Int] = ???
val b: Signal[Int] = ???
val xPlusY: Signal[Int] = Signal(a() + b())
这里,当 a()
被调用时,它会将自己添加到当前评估信号的依赖项列表中。由于无法在其他任何地方访问此信息,因此我们基本上使用线程本地 a.k.a。全局变量。
这个解决方案有一些问题。例如,如果我们不在任何 Signal()
中,我们也可以调用 a()
。另外,好吧,我们必须使用一个全局变量。
解决方案是通过隐式参数将此信息提供给 a()
:我们将签名从
Signal[T]#apply(): T
到
Signal[T]#apply()(implicit triggeredBy: Signal[_])
(请注意,我们可能希望使用一些封装 Signal 的新类型 TriggeredBy
)
这样,此方法的实现将无需 global/thread-local 变量即可访问其原始信号。
但是现在我们必须以某种方式提供这个隐含的。一种选择是同时更改信号创建函数的签名
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.rx3走过的路,从使用的角度来看效果很好。这样也可以让我们再写Signal(a() + b())
更新:更新link 隐式函数解释到更详细的博客文章