看一下purescript-signal示例,我看到一个常见的模式是使用纯更新函数和一个有效的渲染函数:
runSignal $ map render (foldp update initialState input)
但是如果渲染需要保持状态,该怎么办,例如因为渲染中有效的渲染操作会返回以下调用渲染所需的值吗?
展开函数看起来像是要走的路,有这样的东西:
unwrap $ foldp render (pure initialRenderState) (foldp update initialState input)
我做了以下代码来尝试解包,但它没有像我预期的那样工作。我期待它打印从0开始的后续整数,它们之间有一秒的延迟。相反,它每秒打印的不仅是新数字,还包括所有以前的数字。
updateEff :: forall eff. Number -> Eff (console :: CONSOLE | eff) Int -> Eff (console :: CONSOLE | eff) Int
updateEff _ stateEff = do
state <- stateEff
log $ show state
pure $ state + 1
main = do
unwrap $ foldp updateEff (pure 0) (every 1000.0)
同样,这个代码只是尝试解包的一个例子,我知道这些特定的操作可以重写,例如使用纯更新函数和“渲染”函数:runSignal $ map render(foldp update 0(每1000.0))
为什么每次都会重新打印整个数字序列?
为什么每次都会重新打印整个数字序列?
似乎使用foldp
你正在创建信号,建立越来越大的Eff
值。所以例如在第三次迭代之后,你在聚合效果中有这样的东西:
state ← do
state ← do
state ← pure 0
log $ show state
pure $ state + 1
log $ show state
pure $ state + 1
log $ show state
pure $ state + 1
foldp
没有运行你的Eff
值。另一方面,unwrap
根据文档“(...)获取输入信号产生的每个效果并运行它(...)”所以Eff
信号产生的foldp
值在聚合后最后进行评估,所以我们正在观察积累的影响。
但是如果渲染需要保持状态,该怎么办,例如因为渲染中有效的渲染操作会返回以下调用渲染所需的值吗?
我不确定这是否是最好的方法,但你可以使用purescript-refs
的可变引用来保持调用之间的状态 - 这样的事情应该有效:
module Main where
import Prelude
import Control.Monad.Eff.Console (logShow)
import Control.Monad.Eff.Ref (modifyRef, newRef, readRef)
import Signal (unwrap)
import Signal.Time (every)
main = do
s ← newRef 0
let
updateEff _ = do
readRef s >>= logShow
modifyRef s (_ + 1)
unwrap $ map updateEff $ every 1000.0