假设我按下按键并相应地操作代码缓冲区:
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
:)。
为什么IO
中reactive-banana
MonadIO
实际上只有{{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。