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