我需要像accumB
这样的函数,但我希望初始值为Behavior
而不是常量。当初始值改变时,累加器应该“重置”并从该值开始累积。具体而言,对于以下代码和事件序列:
accumB' :: Behavior a -> Event (a -> a) -> IO (Behavior a)
accumB' (stepper 0 ev) ((+) <$> ev')
ev' -> 1
ev' -> 1
ev -> 5
ev' -> 1
accumB'
应返回值0, 1, 2, 5, 6
的步进函数。
这是否可以使用Threepenny目前提供的组合器,还是需要支持动态切换? (我认为答案是'不',所以我目前正试图用accumB'
来实现我自己的IORef
...如果不是,那么accumB'
就像所描述的那样语义定义明确,或者我应该使用Event a
作为时变初始值吗?
答案 0 :(得分:3)
正如J. Abrahamson在评论中指出的那样,Behavior
在时间上不断变化,因此标记Behavior
变化的离散事件没有明确定义。以下是一些替代方案。
如果您使用reactive package并且正在考虑根据Behavior
更改Event
,那么switcher
的类型正确无误:
switcher :: Behavior a -> Event (Behavior a) -> Behavior a
正如您在评论中提到的,如果您要将事件的累加器重置为x
,只需在流中添加const x
事件即可。此示例使用reactive package。
accumBReset :: a -> Event a -> Event (a -> a) -> Behavior a
accumBReset initial resets changes =
accumB initial allChanges
where
allChanges = (fmap const resets) `mappend` changes
在Push-pull Functional Reactive Programming中,Conal Elliott描述了Reactive
类型,类似于仅在离散时刻发生变化的Behavior
。
data Reactive a = a `Stepper` Event a
newtype Event a = Ev (Future (Reactive a))
Reactive
可以转换为标记其更改的事件流,方法是解构它并使用构造函数的右侧,即下一个更改其值的事件
changes :: Reactive a -> Event a
changes (_ `Stepper` nextChange) = nextChange
或者,我们可以获得Reactive的所有值,包括它现在的状态及其未来的所有变化
values :: Reactive a -> Event a
values = Ev . pure
Reactive
值位于reactive package中的FRP.Reactive.Reactive
。
在某些FRP库中,您可以在较低级别执行更多操作。在reactive-banana中,您可以在制作自己的changes
时观察Behavior
到Framework
。这是Reactive.Banana.Framework
&#39; s changes
changes :: Frameworks t => Behavior t a -> Moment t (Event t (Future a))
文档警告说这不是很有意义:
输出,观察
Behavior
何时发生变化。严格来说,
Behavior
表示连续连续的值 在时间上,所以没有明确的事件表明何时 行为改变。然而,出于效率的原因,图书馆提供了一种方法 当行为是阶梯函数时观察变化,例如 由
stepper
创建。没有正式的保证,但这个想法是 该
changes (stepper x e) = return (calm e)
注意:事件的值 在事件处理完成之前不会可用。它可以 仅在
的上下文中使用reactimate'
。