行为被普遍定义为"时变值"s1。
为什么?时间是不同值的依赖性/参数是非常罕见的。
我对FRP的直觉是将行为作为事件变化值来代替;它更常见,更简单,我提出了一个更有效的想法,并且具有足够的可扩展性来支持时间(勾选事件)。
例如,如果编写计数器,则不关心时间/相关时间戳,只关心"增加按钮已单击"和"减少按钮已单击"事件。
如果你写了一个游戏并且想要一个位置/力量行为,你只关心Wasd/箭头键持有的事件等(除非你禁止你的玩家在下午向左移动;多么不公平!).
那么:为什么时间是一个考虑因素呢?为什么是时间戳?为什么有些库(如reactive-banana和reactive会把它扩展到拥有Future和Moment值的程度?为什么要使用事件流而不只是响应事件发生?所有这些似乎都过于复杂化了一个简单的想法(事件变化/事件驱动的价值);收益是什么?我们在这里解决什么问题?(如果可能的话,我还想得到一个具体的例子和一个精彩的解释)。
已经定义了1种行为,所以这里,这里,这里…&几乎所有我遇到的地方。
- 时间是连续的,滴答声不是。""时间"甚至不一定是指实时的,它可以是一个模拟或者任何只流向一个方向的东西。
- @如果这是一个愚蠢的问题,伯吉不好意思,但是为什么我们要关心驱动我们价值观的是连续的还是不连续的呢?
- 不,这不傻。关于连续值的一个好特性是它们不是被驱动的,而是可以被轮询的——它们表示任意"时间"的一个值。
- @伯吉,这不意味着我们对他们(或事件)没有反应吗?例如,reactimate如何工作?如果它只是进行民意调查,直到看到数据流中的变化,那么这将是非常低效的,不是吗?
- 是的,Afaik需要对事件的行为进行抽样,以便谨慎地处理它们。他们是否真的执行效率低下是另一个问题,@conalelliot的"推拉玻璃钢"论文详细说明了这一点。
因为这是我能想到的最简单的方法,可以对行为的概念给出精确的表示(实现独立的含义),包括我想要的操作种类,包括区分和集成,以及跟踪一个或多个其他行为(包括但不限于用户生成的行为)。
Why? time being the dependency/parameter for varying values is very uncommon.
我怀疑你混淆了行为的结构(配方)及其含义。例如,行为可以通过依赖于用户输入之类的东西来构造,可能还需要额外的合成转换。所以这里有食谱。然而,其含义只是时间的函数,与用户输入的时间函数相关。注意,在"函数"这个词的数学意义上,我的意思是:从域(时间)到范围(值)的(确定性)映射,而不是纯粹的程序化描述。
我见过很多问题问为什么时间很重要,为什么连续时间。如果你运用简单的规则,用表示语义学的方式给出一个数学意义(对于函数式程序员来说,这是一种简单而熟悉的方式),问题就会变得更加清楚。
如果你真的想摸索FRP的本质和背后的想法,我建议你阅读我对"功能性反应式编程语言规范"的回答,并遵循指针,包括"什么是功能性反应式编程"。
Behaviors与Events的区别主要在于Behavior现在有一个值,而Event只有在新事件发生时才有一个值。
那么"现在"是什么意思呢?从技术上讲,所有的更改都是作为事件流上的推或拉语义来实现的,因此我们可能只意味着"这个Behavior的最后一个结果事件的最新值"。但在实践中,"现在"这个概念相当复杂,要简单得多。
为什么"现在"更简单的原因可以归结为API。下面是两个来自活性香蕉的例子。
最终,玻璃钢系统必须始终产生某种外部可见的变化。在反应性香蕉中,这是由消耗事件流的reactimate :: Event (IO ()) -> Moment ()功能所促进的。没有办法让Behavior触发外部变化——你总是需要做一些像reactimate (someBehavior <@ sampleTickEvent)这样的事情来在具体的时间对行为进行抽样。
行为是Applicatives与Events不同。为什么?好吧,假设Event是一个应用程序,考虑当我们有两个事件流f和x并编写f <*> x时会发生什么:由于事件发生的时间不同,同时定义f和x的可能性(几乎可以肯定)为0。因此,f <*> x总是指无用的空事件流。
您真正想要的是f <*> x为每个缓存最新的值,并"一直"使用它们的组合值。从事件流的角度来说,这真的是一个令人困惑的概念,因此让我们考虑f和x作为所有时间点的值。现在,f <*> x也被定义为对所有时间点都取值。我们刚刚发明了Behavior。
- 注意,如果时间是离散的,那么f <*> x是为事件定义的。但是现在你必须意识到离散时间!
- 要点2。只要求在所有时间点定义该值,而不是在所有时间点更改该值。我们不需要时间函数的Behavior,除非我们希望某个值在某个事件没有发生的时间点发生变化。
- 如果您的系统经过足够的改进来区分这两个系统(当然是反应性的),这通常是正确的。应该指出,我的回答并不完整,只是说明性的。
- 我有一个问题:现在的值比从(n个自动)状态中读取要简单得多?尤其是当一个值现在无论如何都是状态时。您正在将基本的IOREF(例如)转换为复杂(通常是单体)类型,为每个类型引入基元,等等。
- 这是将实现与接口分离的一种选择。FRP背后的想法是,每当您使用IOREF模型时,您通常都有兴趣谈论事件流。也就是说,IOREF的使用只是一个实现问题,而且效率可能很低。如果您使用FRP原语陈述您的问题,那么库作者可以在代码保持高级和"直观"的同时优化语义的发生方式。老实说,如果你只是想要静态玻璃钢,那么它很简单(单子都消失了)。
- 当选择一个抽象时,经常要考虑的是需要最不强大的抽象。如果您所需要的只是一个Functor,那么您的代码几乎可以处理所有的东西,如果您所需要的只是Applicative,那么这并不是很一般,但是大多数东西仍然可以提供它。如果你需要一个Monad,那没关系;有很多东西可以是单子。如果您需要一个特定的Monad,如IORef的IO,那么您的代码将只适用于IO之上构建的东西。"复杂(通常是单元的)类型"比IORef简单。
- 哦,这是非常正确的,虽然我的意思更多的是在一种状态下,但是,"现在"确实更普遍。那么什么是非静态玻璃钢呢?
- 非静态玻璃钢为Behavior (Behavior a) -> Behavior a。它不能很好地与累积结合起来,并在看似无害的代码中导致主要的性能问题。许多FRP库中的单态CRUFT处理的是在提供动态信号和积累的同时,不可能表达这些性能不佳的程序。
Conal Elliott的推拉式玻璃钢论文描述了事件变化数据,其中唯一有趣的时间点是事件发生时。Reactive事件变化数据是当前值,下一个Event将改变它。Event是在Reactive数据发生变化时的Future点。
1 2
| data Reactive a = a ‘Stepper ‘ Event a
newtype Event a = Ev (Future (Reactive a)) |
Future不需要与之相关联的时间,它只需要表示尚未发生的价值的概念。例如,在带有事件的不纯语言中,未来可以是事件句柄和值。当事件发生时,设置值并提升句柄。
Reactive a在任何时间点上都对a有价值,那么为什么我们需要Behaviors?让我们做一个简单的游戏。在用户按下wasd键之间,在施加的力的加速下,字符仍在屏幕上移动。角色在不同时间点的位置是不同的,即使在中间时间没有发生任何事件。这就是Behavior所描述的——它不仅在所有时间点都有一个值,而且在所有时间点它的值都可能不同,即使没有干预事件。
描述Behavior的一种方法是重复我们刚才所说的。Behaviors是在事件之间可以改变的事物。在事件之间,它们是随时间变化的值或时间函数。
1
| type Behavior a = Reactive (Time -> a) |
我们不需要Behavior,我们可以简单地为时钟计时添加事件,并根据这些计时事件编写整个游戏中的所有逻辑。对于一些开发人员来说,这是不可取的,因为声明游戏内容的代码现在与提供如何实现的代码混合在一起。BehaviorS允许开发者在游戏描述的时变变量和执行该描述的引擎的实现之间分离这种逻辑。