如何在反应性香蕉中创造一元行为

时间:2015-10-12 18:55:57

标签: monads reactive-banana

假设我按下按键并相应地操作代码缓冲区:

let
    bCode = accumB emptyCode eModifications
eCodeChanges <- changes bCode

我想创建另一种行为bEval

bEval = accumB freshEnv (magic eCodeChanges)

将任何代码状态映射到其评估(仅在某些内容发生变化时触发)。

然而,评估发生在monad Interpreter中(从hackage中考虑hint)。我可以实际定义这样的行为bEval吗?我想我可以拖动Interpreter String作为我的行为中的状态,累积currentAccumState >>= eval nextEvent,但我会runInterpreter在哪里实际强制进行评估?

编辑:重要的是,这些操作不仅仅是IO (),而且应该修改某些状态。考虑例如清除缓冲区/重置计数器/在拉链周围移动。

我的想法是这样的:

f :: a -> Maybe (b -> IO b)

mapJustIO :: (a -> Maybe (b -> IO b)) -> Event t a -> Event t (b -> IO b)
mapJustIO f e = filterJust $ f <$> e

accumIO :: a -> Event t (a -> IO a) -> Behaviour t (IO a)
accumIO z e = fold (>>=) (return z) e

我不知道为什么会出现这样的事情。但是,我不知道如何摆脱行为IO:)。

为什么IOreactive-banana MonadIO实际上只有{{1}}?

1 个答案:

答案 0 :(得分:1)

简短的回答是像accumB之类的组合器等只能与函数一起使用。它们无法使用IO monad(或其他类似IO的monad,如Interpreter)中的函数,因为无法以任何有意义的方式定义操作的顺序。

要使用IO操作,Reactive.Banana.Frameworks中的组合器是合适的。例如,您可能想要一个像

这样的函数
mapIO' :: (a -> IO b) -> Event a -> MomentIO (Event b)

有关详情,请参阅previous answer

对于来自Interpreter的{​​{1}} monad,我建议在主线程中运行它,为FRP逻辑分配一个单独的线程,并使用hint s或{{ 1}}用于两者之间的通信。 (我喜欢称之为forklift pattern)。这样,您就可以从TVar访问TChan monad。