为持续可测量的现象创建行为 [英] Creating a Behavior for a continuously measurable phenomenon

查看:139
本文介绍了为持续可测量的现象创建行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从 IO a 创建一个行为ta ,并且使用IO动作的预期语义将在每次行为 sample d时运行:

  { - 语言FlexibleContexts# - } 
import Reflex.Dom
import Control.Monad.Trans
$ b onDemand ::(MonadWidget tm,MonadIO(PullM t))=> IO a - > m(行为ta)

我希望我可以通过执行中测量

  onDemand measure = return $ pull(liftIO measure)

然而,结果 Behavior 在初始度量之后永远不会改变。



我能想出的解决方法是创建一个虚拟的行为,它可以频繁地更改,然后创建一个假依赖关系:

 导入Data.Time.Clock作为时间

hold_ ::(MonadHold tm,Reflex t)=>事件t a - > m(行为t())
hold_ = hold()。 (()<$)

onDemand ::(MonadWidget t m,MonadIO(PullM t))=> IO a - > m(行为t a)
onDemand measure =做
现在< - liftIO Time.getCurrentTime
tick< - hold_ =<<< tickLossy(1/1200)now
return $ pull $ do
_< - sample tick
liftIO measure

然后按预期工作;但既然行为 s只能根据需要采样,这不应该是必要的。



创建一个行为的正确方法是:对于持续,随时都可观察的现象?

解决方案

Spider 中执行此操作看起来不可能。 内部提前推理。



Spider Reflex ,其中一个可能的 行为 s将拉取该值。

 数据行为a 
= BehaviorHold!(保留a)
| BehaviorConst!a
| BehaviorPull!(Pull a)

A Pull ed值包含如何计算当需要时, pullCompute 和一个缓存的值以避免不必要的重新计算, pullValue

  data pull a 
= Pull {pullValue ::!(IORef(Maybe(PullSubscribed a)))
,pullCompute ::!(BehaviorM a)
}

忽略 BehaviorM liftIO 以显而易见的方式提取 IO 计算,它在行为M 需要被抽样。在 Pull 中,您的行为会被观察一次,但不会被重新观察,因为缓存的值不会失效。



缓存值 PullSubscribed一个 包含值 a ,如果此值无效,则需要使其他值无效的列表以及一些无聊的内存管理的东西。

pre $ data pullSubscribed a
= PullSubscribed {pullSubscribedValue ::!a
, pullSubscribedInvalidators ::!(IORef [Weak Invalidator])
- ...无聊的内存
}

Invalidator 是一个量化的 Pull ,足以获得内存引用t o递归地读取invalidators来使失效并将缓存值写入 Nothing



能够不断使我们自己的 BehaviorM 失效。执行时,传递给 BehaviorM 的环境具有自己的invalidator副本,该副本由 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
- ...
}

在内部之外,如果确实存在一种方法来持续对 Behavior 它将涉及 MonadFix(PullM t)实例或通过修复 pull sample

  onDemand ::(Reflex t,MonadIO(PullM t))=> IO a  - >行为ta 
onDemand read = b
其中
b =拉出去
去=做
样品b
liftIO读取

我没有 Reflex 环境来尝试此操作,但我不'我认为结果会很好。


I would like to create a Behavior t a from an IO a, with the intended semantics that the IO action would be run every time the behavior is sampled:

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

onDemand :: (MonadWidget t m, MonadIO (PullM t)) => IO a -> m (Behavior t a)

I hoped I could do this by just executing the measurement in a pull:

onDemand measure = return $ pull (liftIO measure)

However, the resulting Behavior never changes after an initial measurement.

The workaround I could come up with was to create a dummy Behavior that changes "frequently enough" and then create a fake dependency on that:

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

This then works as expected; but since Behaviors can only be sampled on demand anyway, this shouldn't be necessary.

What is the correct way to create a Behavior for a continuous, observable-at-any-time phenomenon?

解决方案

Doing this in Spider looks impossible. Internal reasoning ahead.

In the Spider implementation of Reflex, one of the possible Behaviors is to pull the value.

data Behavior a
   = BehaviorHold !(Hold a)
   | BehaviorConst !a
   | BehaviorPull !(Pull a)

A Pulled value consists of how to compute the value when needed, pullCompute, and a cached value to avoid unnecessary re-computation, pullValue.

data Pull a
   = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
          , pullCompute :: !(BehaviorM a)
          }

Ignoring the ugly environment of BehaviorM, liftIO lifts an IO computation the obvious way, it runs it when the BehaviorM needs to be sampled. In the Pull, your behavior is observed once but isn't re-observed because the cached value isn't invalidated.

The cached value PullSubscribed a consists of the value a, a list of other values that need to be invalidated if this value is invalidated, and some boring memory management stuff.

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

An Invalidator is a quantified Pull that's enough to get the memory reference to recursively read the invalidators to invalidate and write the cached value to Nothing.

To pull constantly we'd like to be able to constantly invalidate our own BehaviorM. When executed, the environment passed to the BehaviorM has a copy of its own invalidator, which is used by dependencies of the BehaviorM to invalidate it when they themselves become invalid.

From the internal implementation of readBehaviorTracked there seems to be no way that the behavior's own invalidator (wi) can ever end up in the list of subscribers that are invalidated when it is sampled (invsRef).

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

Outside of the internals, if there does exist a way to constantly sample a Behavior it would involve a MonadFix (PullM t) instance or mutual recursion through fixing of pull and 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

I don't have a Reflex environment to try this in, but I don't think the results will be pretty.

这篇关于为持续可测量的现象创建行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆