所以我试图了解Sodium的功能反应式编程模型是如何工作的,而且我遇到了一些障碍。 我有一个数字列表,我正在使用“时间”值更新,并且当传入空格字符时,我将添加到此列表中。 运行它的引擎如下。
import FRP.Sodium
type Time = Event Int
type Key = Event Char
type Game a = Time -> Key -> Reactive (Behavior a)
run :: Show a => Game a -> IO ()
run game = do
(dtEv, dtSink) <- sync newEvent
(keyEv, keySink) <- sync newEvent
g <- sync $ do
game' <- game dtEv keyEv
return game'
go g dtSink keySink
return ()
where
go gameB dtSink keySink = do
sync $ dtSink 1
ks <- getLine
mapM_ (sync . keySink) ks
v <- sync $ sample gameB
print v
go gameB dtSink keySink
因此,我正在打印当前值,游戏行为会给每个“滴答”。这是游戏行为的代码。
main :: IO ()
main = run game
game :: Time -> Key -> Reactive (Behavior [Int])
game dt key = do
let spawn = const 0 <$> filterE (==' ') key
rec
bs <- hold [] $ snapshotWith (\s xs -> (s:xs)) spawn updated
updated <- hold [] $ snapshotWith (\t xs -> map (+t) xs) dt bs
return updated
我期望这样做的是输入的每个空格字符,0
被注入列表中。
实际上,每次按下Enter键,我都希望列表中的所有数字都加1。
相反,只有在按空格后,数字才会增加。
有谁知道我哪里出错了?
答案 0 :(得分:1)
经过一番思考后,问题显而易见。
我的代码的问题是我有这种循环依赖,没有考虑到每个行为也取决于它自己的变化的事实。
这意味着每当我尝试将内容添加到列表时,它会将时间更新给出的列表的旧值更改为值,直到时间值更改为止。
为了解决这个问题,我重构了game
行为以合并更新和生成事件,如此。
data GEvent = Alter ([Int] -> [Int])
game :: Time -> Key -> Reactive (Behavior [Int])
game dt key = do
let spawn = const (Alter (\xs -> (0:xs))) <$> filterE (==' ') key
update = (\t -> Alter (\xs -> map (+t) xs)) <$> dt
applyAlter (Alter f) xs = f xs
rec
bs <- hold [] $ snapshotWith applyAlter (merge spawn update) bs
return bs
这可确保在任何一个事件发生时,他们获得最新版本的列表。