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
吗?或者我误解了什么。
这个故事有两个错误。问题给出的例子实际上只暴露了第一个,但是在研究它的过程中我发现 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
的某处使用它。
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
吗?或者我误解了什么。
这个故事有两个错误。问题给出的例子实际上只暴露了第一个,但是在研究它的过程中我发现 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
的某处使用它。