简体   繁体   English

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

[英]Reactive's Monad and Applicative instances in Conal Elloit's FRP

Conal Elliott's paper has the following definitions: Conal Elliott 的论文有以下定义:

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))

I believe the above is correct.我相信以上是正确的。

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

Should rC1 = rC2 ?应该rC1 = rC2吗?

Based on the definition in the paper it appears that "IA1B" and "1AIB" would be included in between "IAIB" and "1A1B" in rC2 .根据论文中的定义,似乎"IA1B" and "1AIB"将包含在"IAIB"中的"IAIB""1A1B" rC2

Doesn't that violate a Monad law (<*>) = ap ?这不违反 Monad 法则(<*>) = ap吗? Shouldn't rC1 = rC2 ?不应该rC1 = rC2吗? Or I am misunderstanding something.或者我误解了一些东西。

Here is the Idris code I am using这是我正在使用的 Idris 代码

There's two bugs in this story.这个故事有两个错误。 The example given by the question actually only exposes the first one, but while working through it I found another more serious bug in the definition of join .问题给出的示例实际上只公开了第一个,但是在解决它的过程中,我发现了join定义中的另一个更严重的错误。

The first bug is minor: it is about handling simultaneous events.第一个错误很小:它是关于处理同时发生的事件。 Your race function allows you to merge simultaneous events.您的race功能允许您合并同时发生的事件。 This is taken advantage of in (<*>) , but then in your definition of join , the These case pretends that the left (inner) event happens first.这是利用 in (<*>) ,但是在您对join的定义中, These情况假设左(内部)事件首先发生。 So if you think of implementing (<*>) using join , and the two behaviors have simultaneous events, join will not merge them.因此,如果您考虑使用join实现(<*>) ,并且这两种行为具有同时发生的事件,则join不会合并它们。

In the case of reactive behaviors, two simultaneous events are undistinguishable from two events that happen very close to each other in time, and in any case, the last one wins, so it should not matter whether the previous one appears in the stream or not.在反应性行为的情况下,两个同时发生的事件与时间上非常接近的两个事件无法区分,无论如何,最后一个获胜,所以前一个是否出现在流中应该无关紧要. This would break down if you start counting events for example, and from that I would simply conclude that such an operation should not be allowed on behaviors.例如,如果您开始计算事件,这将崩溃,因此我可以简单地得出结论,不应允许对行为进行此类操作。

I think the = symbol that appears in laws is often taken too syntactically (if you allow me to call it so), whereas there are various situations, such as this, where it's just more flexible to read it semantically .我认为出现在法律中的=符号在句法上经常被采用(如果你允许我这样称呼它),而有各种情况,例如这种,在语义上阅读它更灵活。 Often they coincide, so it's also hard to really see the point unless you're used to think about it formally.它们通常是一致的,因此除非您习惯于正式考虑,否则也很难真正理解这一点。

From that point of view, it would be reasonable to ignore this as "not a bug".从这个角度来看,将其视为“不是错误”而忽略它是合理的。 It still seems possible to fix the implementation so that the laws do hold syntactically;似乎仍然可以修复实现,以便法律在句法上保持不变; that would make everyone happy at any rate.无论如何,这会让每个人都开心。


To understand the second bug, you must take another look at the meaning of join :: Reactive (Reactive a) -> Reactive a .要理解第二个bug,你必须再看看join :: Reactive (Reactive a) -> Reactive a的含义。

A r :: Reactive (Reactive a) is an evolving behavior. A r :: Reactive (Reactive a)是一种不断进化的行为。 At some time you get behavior x (whose inner variations are not shown), then behavior y , then behavior z ... So schematically it looks like a stream of behaviors有时你会得到行为x (其内部变化没有显示),然后是行为y ,然后是行为z ......所以示意性地它看起来像一个行为流

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

If we think of behaviors as functions of time r :: Time -> (Time -> a) , join samples r at time t , that's another behavior rt :: Time -> a , which itself gets sampled at time t : (join r) t = rtt .如果我们将行为视为时间r :: Time -> (Time -> a) r在时间t join样本r ,这是另一种行为rt :: Time -> a ,它本身在时间t被采样:( (join r) t = rtt In other words, you take the diagonal behavior:换句话说,您采用对角线行为:

x
 y
  z

Thus, if we look back to the event-driven definition of Reactive , it is only natural to forget events as soon as a new behavior appears.因此,如果我们回顾Reactive的事件驱动定义,很自然地会在新行为出现时立即忘记事件。 And indeed, the definition of join as it appears in the paper does that by racing with join <$> urr , which forgets about the inner behavior ur .事实上,论文中出现的join定义是通过与join <$> urr ,它忘记了内部行为ur However that only accounts for one side of the diagonal:然而,这只占对角线的一侧:

x
yy
zzz

Because Reactive behaviors are streams of events, inner streams of a Reactive (Reactive a) can contain events that occur before the stream's own appearance.因为Reactive性行为是事件流, Reactive (Reactive a)内部流可以包含在流自身出现之前发生的事件。 Thus, when you get a Future (Reactive a) , you also need a way to truncate events from before the Future happens:因此,当您获得Future (Reactive a) ,您还需要一种方法来截断Future发生之前的事件:

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

and use that somewhere in join .并在join某个地方使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM