行为无处不在地定义为“时变值” 1 。
为什么呢?时间是变化值的依赖/参数非常罕见。
我对FRP的直觉是将行为改为事件变化值;它更常见,更简单,我提出了更多有效的想法,并且可扩展到足以支持时间(tick事件)。
例如,如果你写一个计数器,你不关心时间/相关的时间戳,你只关心“点击增加按钮”和“减少按钮点击”事件。
如果你写一个游戏并想要一个位置/强制行为,你只关心WASD /箭头键举行的事件等(除非你禁止你的玩家在下午向左移动;多么不公平!)。
那么:为什么时间是一个考虑因素?为什么时间戳?为什么有些库(例如reactive-banana
,reactive
)会将其提升到Future
,Moment
值的范围内?为什么使用事件流而不是仅响应事件发生?所有这一切似乎都过于复杂化了一个简单的想法(事件变化/事件驱动值);有什么好处?我们在这里解决了什么问题? (如果可能的话,我也希望得到一个具体的例子和一个精彩的解释。)
答案 0 :(得分:12)
Behavior
与Event
的不同之处主要在于Behavior
目前的值 ,而Event
每次只有一个值一个新的事件进来了。
那么我们的意思是"现在"?从技术上讲,所有更改都是作为事件流上的推送或拉动语义实现的,因此我们只能意味着这个Behavior
"的最后一个事件的最新值。但这在实践中是一个相当毛茸茸的概念"现在"更简单。
为什么"现在"更简单归结为API。以下是Reactive Banana的两个例子。
最终,FRP系统必须始终产生某种外部可见的变化。在Reactive Banana中,使用事件流的reactimate :: Event (IO ()) -> Moment ()
函数促进了这一点。没有办法让Behavior
触发外部变更 - 你总是需要像reactimate (someBehavior <@ sampleTickEvent)
那样在具体时间对行为进行抽样。
行为Applicative
与Event
不同。为什么?好吧,让我们假设Event
是一个应用程序,并考虑当我们有两个事件流f
和x
并写f <*> x
时会发生什么:因为事件都发生了在不同时间同时定义f
和x
的机会是(almost certainly)0。所以f <*> x
总是意味着空事件流是无用的。
你真正想要的是f <*> x
为每个值缓存最新的值,并且总是采用它们的组合值&#34;所有的时间&#34;。这是一个非常令人困惑的概念,无法根据事件流进行讨论,因此我们可以考虑将f
和x
视为所有时间点的值。现在f <*> x
也被定义为为所有时间点获取值。我们刚刚发明了Behavior
s。
答案 1 :(得分:12)
因为这是我能想到的最简单的方法,可以为行为概念提供精确的表示(与实现无关的意义),包括我想要的各种操作,包括区分和整合,以及跟踪一个或多个其他行为(包括但不限于用户生成的行为)。
为什么呢?时间是变化值的依赖/参数非常罕见。
我怀疑您将行为的构造(配方)与其含义混淆。例如,可以通过依赖于诸如用户输入之类的东西来构造行为,可能还有其他合成变换。所以有配方。然而,含义仅仅是时间的函数,与用户输入的时间函数有关。请注意,通过&#34; function&#34;,我的意思是在数学意义上的单词:a(确定性)映射从域(时间)到范围(值),不是在某种意义上这是一个纯粹的程序化描述。
我已经看到很多问题,询问为什么时间很重要以及为什么连续时间。如果你应用简单的规则来赋予指称语义风格的数学意义(对于函数式程序员来说是一种简单而熟悉的风格),问题就会变得更加清晰。
如果您真的想了解FRP的精髓和思考,我建议您阅读my answer to "Specification for a Functional Reactive Programming language"并按照指示进行操作,包括"What is Functional Reactive Programming"。
答案 2 :(得分:4)
Conal Elliott's Push-Pull FRP论文描述了事件变化数据,其中唯一有趣的时间点是事件发生时。 Reactive
事件变化数据是当前值,下一个Event
将更改它。 Event
是事件变化Future
数据中的Reactive
点。
data Reactive a = a ‘Stepper ‘ Event a
newtype Event a = Ev (Future (Reactive a))
Future
并不需要有时间与之关联,只需要表示尚未发生的价值的想法。例如,在带有事件的不纯语言中,未来可以是事件句柄和值。当事件发生时,您设置值并抬起句柄。
Reactive a
在所有时间点都有a
的值,那么为什么我们需要Behavior
?让我们做一个简单的游戏。在用户按下WASD键之间,由施加的力加速的角色仍然在屏幕上移动。即使在中间时间内没有发生任何事件,角色在不同时间点的位置也是不同的。这就是Behavior
描述的东西 - 不仅在所有时间点都有价值,而且它的价值在所有时间点都可能不同,即使没有干预事件也是如此。
描述Behavior
的一种方法是重复我们刚才所说的内容。 Behavior
是可以在事件之间发生变化的事物。在中间事件中,它们是时变值或时间函数。
type Behavior a = Reactive (Time -> a)
我们不需要Behavior
,我们可以简单地为时钟刻度添加事件,并根据这些刻度事件在整个游戏中编写所有逻辑。这对于一些开发人员来说是不可取的,因为声明我们的游戏的代码现在与提供 实现的代码混合在一起。 Behavior
允许开发人员根据时变变量和执行该描述的引擎的实现,在游戏描述之间分离这个逻辑。