1

我需要一个类似的函数accumB,但我希望初始值是 aBehavior而不是常数。当初始值改变时,累加器应该“重置”并从该值开始累加。具体来说,对于以下代码和事件序列:

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'IORefs 来实现我自己的......)如果不是,accumB'如上所述,在语义上是明确定义的,或者我应该Event a在我的时间使用- 改变初始值?

4

1 回答 1

3

正如 J. Abrahamson 在评论中指出的那样,aBehavior随时间连续变化,因此标记为 a 变化的离散事件Behavior没有得到很好的定义。这里有一些替代方案。

按事件改变行为

如果您正在使用响应式包并且正在考虑Behavior基于 s 更改 s Event,那么switcher具有正确的类型:

switcher :: Behavior a -> Event (Behavior a) -> Behavior a

通过重置累积事件

正如您在评论中提到的,如果您想将事件上的累加器重置为x,只需将const x事件放入流中即可。这个例子使用了响应式包

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仅在离散时刻发生变化的 a。

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值在FRP.Reactive.Reactive反应中。

从较低级别的行为中获取更改

在某些 FRP 库中,您可以在较低级别执行更多操作。在反应香蕉changes中,您可以Behavior在制作自己的Framework. 这是 Reactive.Banana.Framework's的类型changes

changes :: Frameworks t => Behavior t a -> Moment t (Event t (Future a))

文档警告说这并没有真正的意义:

输出,观察a何时Behavior变化。

严格来说,aBehavior表示一个随时间连续变化的值 ,因此没有明确定义的事件来指示行为何时发生变化。

尽管如此,出于效率的原因,该库提供了一种在行为是阶跃函数时观察变化的方法,例如由stepper. 没有正式的保证,但想法是

changes (stepper x e) = return (calm e)

注意:在事件处理完成之前,事件的值将不可用。它只能在 的上下文中使用reactimate'

于 2014-07-24T20:53:03.357 回答