我正在试验threepenny-gui,试图学习FRP接口。我想避免使用 accumE/accumB 而不是 IORef:s 的所有显式共享状态。我有四个不同的信号(启动、停止、定时器、重置),它们都会影响全局状态和用户界面。我accumE w $ concatenate <$> unions [e0, e1, e2, e3]
用来使事件共享相同的状态w
。这是一个简短的片段,它抓住了它的本质(只有一个信号):
data World = World { intW :: Int , runW :: UI () }
main :: IO ()
main = startGUI defaultConfig setup
setup :: Window -> UI ()
setup _ = do
(e0, fire) <- liftIO UI.newEvent
let e0' = action <$ e0
e <- accumE (World 0 (return ())) e0'
onEvent e $ \w -> void $ runW w
replicateM_ 5 . liftIO $ fire ()
where
action :: World -> World
action w = w { intW = succ $ intW w
, runW = liftIO . print $ intW w }
这似乎工作正常(尽管我想知道它是否正常)。但是,如果我改为将事件更改为具有类型Event (UI World -> UI World)
(并删除 runW 字段),事情就会变得混乱:
data World = World { intW :: Int } deriving (Show)
main :: IO ()
main = startGUI defaultConfig setup
setup :: Window -> UI ()
setup _ = do
(e0, fire) <- liftIO UI.newEvent
let e0' = action <$ e0
e <- accumE (return (World 0)) e0'
onEvent e void
replicateM_ 5 . liftIO $ fire ()
where
action :: UI World -> UI World
action world = do
w <- world
let w' = w { intW = succ $ intW w }
liftIO $ print w'
return w'
似乎所有 UI 操作都以某种方式累积,并随着每个事件执行越来越多的次数!我认为 accumE 就像一个折叠,除非明确累积,否则累积状态会被替换。一般来说,处理这个问题的正确/最佳方法是什么?