使用可重置的初始值累积事件

时间:2014-07-21 22:13:42

标签: haskell frp threepenny-gui

我需要像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作为时变初始值吗?

1 个答案:

答案 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时观察BehaviorFramework。这是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'

的上下文中使用