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