如何在 reactive-banana 中创建单子行为
How to create monadic behaviour in reactive-banana
假设我捕捉到按键并相应地操作代码缓冲区:
let
bCode = accumB emptyCode eModifications
eCodeChanges <- changes bCode
我想创建另一个行为bEval
bEval = accumB freshEnv (magic eCodeChanges)
它将代码的任何状态映射到它的求值(只有当某些事情真正发生变化时才会触发)。
但是,评估发生在 monad Interpreter
中(认为 hint
来自 hackage)。我真的可以定义这样的行为 bEval
吗?我想我可以将 Interpreter String
作为我行为中的状态,用 currentAccumState >>= eval nextEvent
累积,但是我 runInterpreter
在哪里才能真正强制评估?
编辑:重要的是动作不仅仅是IO ()
,而且应该修改一些状态。例如,考虑在拉链周围清除 buffer/reseting counter/moving。
我的想法是这样的:
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
:).
为什么 reactive-banana
中没有出现 IO
实际上只有 MonadIO
?
简短的回答是 accumB
等组合器只能与 纯 函数一起使用。它们无法使用来自 IO monad(或其他类似 IO 的 monad,如 Interpreter
)的函数,因为无法以任何有意义的方式定义操作顺序。
要使用 IO 操作,Reactive.Banana.Frameworks
中的组合器是合适的。例如,您可能想要一个像
这样的函数
mapIO' :: (a -> IO b) -> Event a -> MomentIO (Event b)
请参阅 了解更多信息。
对于来自 hint
的 Interpreter
monad,我建议 运行 它在主线程中,为你的 FRP 逻辑分叉一个单独的线程,并使用 TVar
s 或 TChan
用于两者之间的通信。 (我喜欢称其为 forklift pattern)。这样,您可以从 IO
.
访问 Interpreter
monad
假设我捕捉到按键并相应地操作代码缓冲区:
let
bCode = accumB emptyCode eModifications
eCodeChanges <- changes bCode
我想创建另一个行为bEval
bEval = accumB freshEnv (magic eCodeChanges)
它将代码的任何状态映射到它的求值(只有当某些事情真正发生变化时才会触发)。
但是,评估发生在 monad Interpreter
中(认为 hint
来自 hackage)。我真的可以定义这样的行为 bEval
吗?我想我可以将 Interpreter String
作为我行为中的状态,用 currentAccumState >>= eval nextEvent
累积,但是我 runInterpreter
在哪里才能真正强制评估?
编辑:重要的是动作不仅仅是IO ()
,而且应该修改一些状态。例如,考虑在拉链周围清除 buffer/reseting counter/moving。
我的想法是这样的:
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
:).
为什么 reactive-banana
中没有出现 IO
实际上只有 MonadIO
?
简短的回答是 accumB
等组合器只能与 纯 函数一起使用。它们无法使用来自 IO monad(或其他类似 IO 的 monad,如 Interpreter
)的函数,因为无法以任何有意义的方式定义操作顺序。
要使用 IO 操作,Reactive.Banana.Frameworks
中的组合器是合适的。例如,您可能想要一个像
mapIO' :: (a -> IO b) -> Event a -> MomentIO (Event b)
请参阅
对于来自 hint
的 Interpreter
monad,我建议 运行 它在主线程中,为你的 FRP 逻辑分叉一个单独的线程,并使用 TVar
s 或 TChan
用于两者之间的通信。 (我喜欢称其为 forklift pattern)。这样,您可以从 IO
.
Interpreter
monad