在 FRP 中实现快照

Posted

技术标签:

【中文标题】在 FRP 中实现快照【英文标题】:Implementing snapshot in FRP 【发布时间】:2012-03-26 10:53:57 【问题描述】:

我正在 Scala 中实现 FRP 框架,但我似乎遇到了问题。出于某种想法,这个问题我决定限制我的框架的公共接口,因此只能在“现在”中评估行为,即:

behaviour.at(now) 

这也符合 Conal 在 Fran 论文中的假设,即仅在增加的时间对行为进行评估/采样。它确实限制了 Behaviors 的转换,但除此之外,我们发现自己在表示某些输入的 Behaviors 方面存在巨大问题:

val slider = Stepper(0, sliderChangeEvent) 

使用此行为,评估未来值将是不正确的,评估过去值将需要无限量的内存(必须存储“滑块”事件中使用的所有事件)。

鉴于此限制,我在对行为的“快照”操作规范方面遇到问题。我的问题最好用一个例子来解释(使用上面提到的滑块):

val event = mouseB // an event that occurs when the mouse is pressed 
val sampler = slider.snapshot(event) 
val stepper = Stepper(0, sampler) 

我的问题是,如果在执行此代码时发生了“mouseB”事件,那么“步进器”的当前值将是“滑块”的最后一个“样本”(最后一次发生时的值) )。如果最后一次发生的时间是过去,那么我们最终将使用过去的时间评估“滑块”,这违反了上面的规则集(以及您的原始假设)。我可以看到几种方法来解决这个问题:

    我们“记录”过去(在事件中保留所有过去发生的事件)允许评估过去时间的行为(使用无限量的内存) 我们修改“快照”以采用时间参数(“此时间后的样本”)并强制该时间 >= 现在 在一个更古怪的举动中,我们可以以某种方式将 FRP 对象的创建限制为程序的初始设置,并且仅在此设置完成后才开始处理事件/输入

我也可以简单地不实现“sample”或删除“stepper”/“switcher”(但我真的不想做这两件事)。有人对此有任何想法吗?我在这里误解了什么吗?

【问题讨论】:

你知道Reactive,不是吗? Reactive 很酷,但它打破了 FRP 中的一些想法。例如,它没有连续行为的概念:反应中的信号在不同值之间随时间离散地变化。我最初很困惑这如何适合 FRP,并在不久前问了这个问题:***.com/questions/7451317/… 另外,据我所知,Reactive 实际上并没有任何像“快照”这样的功能。 【参考方案1】:

哦,我明白你的意思了。

我认为,您的“您只能在‘现在’取样”的限制还不够严格。它需要更强大一点,以避免回顾过去。由于您使用的是now 的环境概念,我将根据它定义行为构造函数(只要now 不能仅通过执行定义来推进,根据我的最后一个答案,这会变得混乱) .例如:

Stepper(i,e) 是在区间 [now,e1] 中具有值 i 的行为(其中 e1now 之后第一次出现e 的时间,以及之后最近出现的e 的值。

有了这个语义,你对 stepper 的值导致你陷入这个难题的预测被取消了,步进器现在的值为 0。我不知道这个语义是否适合你,但它对我来说似乎很自然。

【讨论】:

这是一种非常好的展示方式。我只是对试图找到一种方法来维护原始(可爱的)语义而不使用无限量的内存感到沮丧。我注意到一些实现因此删除了“步进器”和“切换器”,但它们看起来非常强大。嗯嗯。 @seadowg,Fran 语义很好,但我们迄今为止未能有效地实现它们(它们总是有这样的空间泄漏,以及相应的缓慢)。我个人在这个问题上花了大约 6 个月的时间。我们也无法确定不可能的任何原因,因此可能仍然存在解决方案。可以说它并不明显。 @luqui:在this blog post 中,Heinrich Apfelmus 指向a paper,这应该解释了为什么标准动态事件切换类型本质上是错误的。我还不够专业,看不懂,但你见过吗,如果见过,你觉得它没有说服力吗? (有点跑题了,我知道,但是当我看到它时,我认为这在 FRP 社区中是众所周知的。) @ehird:基本上,论文(实际上是幻灯片)认为,FRP 对应于一种类型系统中的逻辑,即时间逻辑。这是非常美丽的。但是要让它工作,你必须添加一个额外的类型参数,然后你会发现 switcher 的原始类型不好。 "可以说它并不明显。"回想起来除外。【参考方案2】:

据我所知,您担心竞争条件:如果在代码执行时发生事件会发生什么。

纯功能代码不希望知道它会被执行。函数式技术在纯粹的环境中处于最佳状态,因此执行代码的顺序无关紧要。摆脱这种困境的一种方法是假装每次更改都发生在一段敏感的(可能是内部的)命令式代码中;假设 FRP 框架中的任何函数声明发生在 0 时间,因此在声明期间不可能发生任何更改。

任何人都不应该在声明行为和事物的代码段中睡觉,或真正做任何时间敏感的事情。本质上,与 FRP 对象一起工作的代码应该是纯的,那么您就没有任何问题。

这并不一定排除在多个线程上运行它,但为了支持您可能需要重新组织内部表示。欢迎来到 FRP 库实现的世界——我怀疑您的内部表示会在此过程中多次波动。 :-)

【讨论】:

有趣的地方。我不担心在执行此代码时是否会发生某些事情(所有这些都可能是个问题),但如果 mouseB 之前发生过。很简单,给定“快照”的定义,返回事件中的“最后一次”出现将是 (t, slider.at(t)) where t 【参考方案3】:

我对你的困惑感到困惑。我看到的方式是Stepper 将在事件发生时将行为“设置”为新值。那么,会发生以下情况:

mouseB 事件发生的瞬间,slider 行为的值将被读取 (snapshot)。该值将被“设置”到行为stepper

所以,Stepper 确实会“记住”过去的值;关键是它只记住过去的最新值,而不是全部。

在语义上,最好将Stepper 建模为luqui 提出的函数。

【讨论】:

是的,如果我们不允许过去的抽样。我的问题是,如果我们使用过去发生过的事件调用行为的快照,我们将使用过去的时间(我不想允许)对该行为进行采样。步进器用于帮助示例。 @seadowg:啊,我明白了。你有两个选择:要么你确保在某个时候,过去就在现在,即你已经“走过了过去的事件”。或者你必须修剪你刚刚意识到的过去发生的事情。这些是避免时间泄漏的唯一方法(= 记住过去无限量的数据)。一般来说,累积和动态事件切换不能随意组合,必须有一些限制。

以上是关于在 FRP 中实现快照的主要内容,如果未能解决你的问题,请参考以下文章

SQOOP增量抽取时,在HIVE中实现类似Oracle的merge操作

在android中以编程方式拍摄设备快照

在 VBA 中实现接口的事件

在 C# 中实现接口与显式实现接口 [重复]

推拉式FRP在实现游戏时有帮助吗?

是否可以在 GraphQL 中实现多个接口?