Conal Elloit 的 FRP 中的 Reactive 的 Monad 和 Applicative 实例

Reactive's Monad and Applicative instances in Conal Elloit's FRP

Conal Elliott's paper 具有以下定义:

Future a = (Time, a)
Stepper :: a -> Event a -> Reactive a
Ev :: Future (Reactive a) -> Event a
never : Event a
instance Monad Reactive
rA :: Reactive String
rA = "IA" `Stepper` (Ev (1, "1A" `Stepper` never))

rB :: Reactive String
rB = "IB" `Stepper` (Ev (1, "1B" `Stepper` never))

rC1 :: Reactive String
rC1 = (++) <$> rA <*> rB
rC1 = "IAIB" `Stepper` (Ev (1, "IA1B" `Stepper` never))

我相信以上是正确的。

rC2 :: Reactive String
rC2 = rA >>= (\a -> (a++) <$> rB)

应该rC1 = rC2吗?

根据论文中的定义,"IA1B" and "1AIB" 似乎包含在 rC2 中的 "IAIB""1A1B" 之间。

这不违反 Monad 定律 (<*>) = ap 吗?不应该 rC1 = rC2 吗?或者我误解了什么。

Here is the Idris code I am using

这个故事有两个错误。问题给出的例子实际上只暴露了第一个,但是在研究它的过程中我发现 join.

定义中的另一个更严重的错误

第一个错误很小:它与处理同时发生的事件有关。您的 race 函数允许您合并同时发生的事件。 (<*>) 中利用了这一点,但随后在 join 的定义中,These 情况假装左侧(内部)事件首先发生。所以如果你想到用join实现(<*>),并且这两个行为有同时发生的事件,join就不会合并

在反应行为的情况下,两个同时发生的事件与两个发生时间非常接近的事件无法区分,无论如何,最后一个获胜,所以前一个是否出现应该无关紧要是否在流中。例如,如果您开始对事件进行计数,这将会崩溃,从中我可以简单地得出结论,不应允许对行为进行此类操作。

我认为法律中出现的=符号也经常被句法(如果你允许我这样称呼),然而有各种情况,就像这样,在语义上阅读起来更加灵活。它们通常是重合的,所以也很难真正看出重点,除非你习惯于正式地考虑它。

从这个角度来看,将其忽略为 "not a bug" 是合理的。似乎仍然可以修复实现,以便法律在句法上成立;无论如何都会让每个人都开心。


要理解第二个bug,必须再看一下join :: Reactive (Reactive a) -> Reactive a的意思。

A r :: Reactive (Reactive a) 是一种不断发展的行为。有时你会得到行为 x(其内部变化未显示),然后是行为 y,然后是行为 z...所以示意性地看起来像行为流

xxxxx...
yyyyy...
zzzzz...
...

如果我们将行为视为时间的函数 r :: Time -> (Time -> a)join 个样本 r 在时间 t,那是另一种行为 r t :: Time -> a,它本身在时间 t 被采样:(join r) t = r t t。换句话说,你采取对角线行为:

x
 y
  z

因此,如果我们回顾 Reactive 的 event-driven 定义,一旦出现新的行为就很自然地忘记了事件。事实上,论文中出现的 join 的定义是通过与 join <$> urr 竞赛来实现的,它忘记了内部行为 ur。然而,这只占对角线的一侧:

x
yy
zzz

因为 Reactive 行为是事件流,Reactive (Reactive a) 的内部流可以包含在流自身出现之前发生的事件。因此,当您获得 Future (Reactive a) 时,您还需要一种方法来截断 Future 发生之前的事件:

actualize :: Future (Reactive a) -> Future (Reactive a)
-- Exercise for the reader

并在 join 的某处使用它。