1

所以我试图了解 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 

我希望这样做是在输入每个空格字符时,将 a0注入列表中。实际上,每次按下 enter 时,我希望列表中的所有数字都增加一。相反,只有在我按下空格后,数字才会增加。有谁知道我要去哪里错了?

4

1 回答 1

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

这可确保当任一事件发生时,它们都会获得最新版本的列表。

于 2013-11-27T03:37:59.897 回答