好吧,如果 FRP 库公开了一种绑定到外部事件的方法——例如一个现有的基于事件的框架——那么它必须提供与之等效的功能,否则它无法与外界交互。
但是,问题实际上是:“外部”是什么意思? FRP 系统本身通常被认为是纯的,因此从 FRP 系统内部执行像event.occur(now, 5) 这样的副作用代码的想法甚至没有意义。通常,当然,提供了响应 FRP 事件执行此类代码的工具,但这通常不被视为纯编程模型的一部分,而是作为将网络作为接口的工具与外面的世界融为一体。
所以,在我看来,有两种可能的方式来解释这个问题:
-
是否可以从 FRP 系统外部触发事件?——肯定可以,因为它是与外部世界交互所必需的,但这并不影响 FRP 本身的编程模型。李>
-
是否可以从 FRP 系统的“内部”触发事件,假设有一些设施可以执行副作用代码以响应事件? - 也可以,因为允许正常的副作用代码引发事件但在响应事件执行的代码中禁止它似乎是一个非常奇怪(且可规避)的限制,因为该设施的目的是与外部世界交互。
确实,即使您明确禁止,也可能导致类似于 #2 的情况:考虑进行设置,以便在事件 buttonClicked 触发时执行 switchToWindow 3,例如(使用reactive-banana 表示法):
reactimate (switchToWindow 3 <$ buttonClicked)
说我们有一个活动
newWindowFocused :: Event Int
我们设置的反应会导致newWindowFocused 事件被触发,即使从因事件执行的内部代码中触发事件被阻止。
现在,到目前为止,我所说的一切都只涉及“外部”事件:那些不是用纯 FRP 表示的,而是明确创建来表示在 FRP 系统之外发生在外部世界中的事件。如果您要问是否应该有一种工具可以在 purely-defined 事件中引起特殊事件,那么我的回答是:绝对不!这破坏了系统的含义,因为突然fmap f (union e1 e2) 并不意味着“当e1 或e2 出现值x 时出现值f x”,而是“出现值f x”当 e1 或 e2 出现值 x... 或某些外部代码随机决定触发它时”。
这样的设施不仅会使对 FRP 系统行为的推理在本质上毫无意义,1它还会违反引用透明性:如果您构造两个等效于 fmap f (union e1 e2) 的事件,那么您可以通过触发一个并注意到另一个没有发生来区分它们。您根本无法在所有情况下都阻止这种情况:想象fmap g (union e1 e2),其中f 计算与g 相同的函数;函数的相等性是不可判定的:)
当然,完全可以用不纯的语言实现FRP,但我认为提供一种违反FRP系统本身的引用透明性的方法是一件非常糟糕的事情,因为它毕竟是一个纯模型。
如果我理解正确,您对 API 中的这个缺陷的解决方案(即公开暴露 occur,这会破坏我上面谈到的等效事件的引用透明度等)将是使 occur 内部到你的Event 班级,这样它就不能从外面使用。我同意,如果您在内部需要 occur,这是正确的解决方案。我也同意,如果你的外部事件的实现是通过子类化Event 来完成的,那么将它暴露给子类是合理的。这属于“外部世界胶水”,它不属于 FRP 模型本身的范围,所以让它以这种方式“打破规则”的能力是完全可以的——毕竟,这本质上就是它的目的 : 用副作用干扰系统 :)
所以,总而言之:
不,事件不应公开此接口。
是的,你的想法是对的 :)
1 当然,您可以争辩说外部事件会这样做句号,因为系统的整个行为最终取决于连接到外部世界,但这不是真的:是的,你不能真正假设外部事件本身,但你仍然可以依靠你建立的一切来遵守规则他们的建筑。为每个事件提供“外部点火”设施意味着没有任何建筑有任何法律。