为持续可测量的现象创建行为

Cac*_*tus 11 haskell ghcjs reflex

我想创建一个Behavior t a来自一个IO a预期的语义,每次行为是sampled 时都会运行IO动作:

{- language FlexibleContexts #-}
import Reflex.Dom
import Control.Monad.Trans

onDemand :: (MonadWidget t m, MonadIO (PullM t)) => IO a -> m (Behavior t a)
Run Code Online (Sandbox Code Playgroud)

我希望我可以只执行这样做measurementpull:

onDemand measure = return $ pull (liftIO measure)
Run Code Online (Sandbox Code Playgroud)

但是,在Behavior初始化之后,结果永远不会改变measure.

我可以想到的解决方法是创建一个Behavior"频繁"更改的虚拟对象,然后创建一个假依赖:

import Data.Time.Clock as Time

hold_ :: (MonadHold t m, Reflex t) => Event t a -> m (Behavior t ())
hold_ = hold () . (() <$)

onDemand :: (MonadWidget t m, MonadIO (PullM t)) => IO a -> m (Behavior t a)
onDemand measure = do
    now <- liftIO Time.getCurrentTime
    tick <- hold_ =<< tickLossy (1/1200) now
    return $ pull $ do
        _ <- sample tick
        liftIO measure
Run Code Online (Sandbox Code Playgroud)

然后按预期工作; 但是因为Behaviors只能按需采样,所以这不是必需的.

什么是创建Behavior连续,可观察的任何时间现象的正确方法?

Cir*_*dec 4

这样做Spider看起来不可能。Internal提前推理。

Spider的实现中Reflex,可能的方法之一Behavior就是拉取值。

data Behavior a
   = BehaviorHold !(Hold a)
   | BehaviorConst !a
   | BehaviorPull !(Pull a)
Run Code Online (Sandbox Code Playgroud)

edPull值由如何在需要时计算该值pullCompute和一个缓存值组成,以避免不必要的重新计算pullValue

data Pull a
   = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
          , pullCompute :: !(BehaviorM a)
          }
Run Code Online (Sandbox Code Playgroud)

忽略丑陋的环境BehaviorM,以明显的方式liftIO提升计算,它在需要采样时运行它。在 中,您的行为会被观察一次,但不会再次被观察,因为缓存的值并未失效。IOBehaviorMPull

缓存的值PullSubscribed a由 value a、如果该值无效则需要无效的其他值的列表以及一些无聊的内存管理内容组成。

data PullSubscribed a
   = PullSubscribed { pullSubscribedValue :: !a
                    , pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
                    -- ... boring memory stuff
                    }
Run Code Online (Sandbox Code Playgroud)

AnInvalidator是一个量化值Pull,足以获取内存引用以递归读取无效器以使其无效并将缓存值写入Nothing

为了不断地拉动,我们希望能够不断地使我们自己的BehaviorM. 执行时,传递给 的环境具有BehaviorM其自己的失效器的副本,该副本由 的依赖项使用,BehaviorM以在它们本身失效时使其失效。

从内部实现readBehaviorTracked来看,行为自己的失效器 ( ) 似乎不可能wi出现在对其进行采样 ( ) 时失效的订阅者列表中invsRef

    a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) $ Just (wi, parentsRef)
    invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
    -- ...
    let subscribed = PullSubscribed
          { pullSubscribedValue = a
          , pullSubscribedInvalidators = invsRef
          -- ...
          }
Run Code Online (Sandbox Code Playgroud)

在内部之外,如果确实存在一种不断采样 a 的方法,Behavior它将涉及MonadFix (PullM t)通过固定pull和进行实例或相互递归sample

onDemand :: (Reflex t, MonadIO (PullM t)) => IO a -> Behavior t a
onDemand read = b
    where
        b = pull go
        go = do
             sample b
             liftIO read
Run Code Online (Sandbox Code Playgroud)

我没有Reflex环境可以尝试,但我认为结果不会很好。