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"
将包含在 "IAIB"
之间和"1A1B"
在rC2
.
这不违反 Monad 定律 (<*>) = ap
?不应该rC1 = rC2
?或者我误解了什么。
最佳答案
这个故事有两个错误。问题给出的例子实际上只暴露了第一个,但是在研究它的过程中,我发现 join
的定义中存在另一个更严重的错误。 .
第一个错误很小:它与处理同时发生的事件有关。您的race
功能允许您合并同时发生的事件。 (<*>)
中利用了这一点。 ,但是在 join
的定义中,These
case 假装左侧(内部)事件首先发生。因此,如果您考虑实现 (<*>)
使用join
,并且这两种行为具有同时发生的事件,join
不会合并它们。
在 react 行为的情况下,两个同时发生的事件与两个在时间上非常接近发生的事件无法区分,并且无论如何,最后一个事件获胜,因此前一个事件是否出现在事件中并不重要流或不流。例如,如果您开始对事件进行计数,这种情况就会崩溃,因此我会简单地得出结论,不应在行为上允许进行此类操作。
我认为=
法律中出现的符号通常在语法上被采用得太过(如果你允许我这样调用它的话),而在很多情况下,比如这个,从语义上理解它会更灵活。它们通常是一致的,因此除非您习惯于正式思考,否则很难真正理解这一点。
从这个角度来看,将其视为“不是错误”而忽略它是合理的。似乎仍然有可能修复实现,以便法律在语法上确实成立;无论如何,这会让每个人都高兴。
要理解第二个错误,您必须再看一下join :: Reactive (Reactive a) -> Reactive a
的含义。 .
一个r :: Reactive (Reactive a)
是一种不断发展的行为。有时你会得到行为 x
(其内部变化未显示),则行为 y
,然后行为z
...从原理上讲,它看起来像一个行为流
xxxxx...
yyyyy...
zzzzz...
...
如果我们将行为视为时间的函数 r :: Time -> (Time -> a)
, join
sample r
当时t
,这是另一种行为 r t :: Time -> a
,其本身在时间 t
进行采样:(join r) t = r t t
。换句话说,您采取对角线行为:
x
y
z
因此,如果我们回顾 Reactive
的事件驱动定义,一旦出现新行为,忘记事件是很自然的。事实上, 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
中的某个地方使用它.
关于haskell - Conal Elloit 的 FRP 中的 Reactive 的 Monad 和 Applicative 实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57080085/