【问题标题】:Sampling a behaviour from outside network从外部网络采样行为
【发布时间】:2016-01-12 16:23:15
【问题描述】:

由于钠已被作者deprecated ,我试图将我的代码移植到反应香蕉。但是,两者之间似乎存在一些我很难克服的不一致之处。

例如,在钠中,很容易检索行为的当前值:

retrieve :: Behaviour a -> IO a
retrieve b = sync $ sample b

我没有看到如何在反应香蕉中做到这一点

(我想要这个的原因是因为我试图将行为导出为 dbus 属性。可以从其他 dbus 客户端查询属性)

编辑:替换了“民意调查”这个词,因为它具有误导性

【问题讨论】:

  • 在概念层面上,采样Behavior 仅在Moment 的上下文中才有意义,即在特定的时间点,IO 不提供。这不仅仅是一个理论问题,而且对于实现的内部一致性很重要,所以我在犹豫是否要添加这样的功能。您能否详细说明您想在其中使用它(dbus)的具体上下文?很有可能它可以用不同的方式表达。
  • 事实证明,我可以使用 reactive-banana 提供的工具重新实现相同的行为:gist。我在这个例子中使用 unsafePerformIO 来尽可能接近钠的语义,但这绝不是必要的(我只需要传递 IO 操作)。所以问题就变成了:有没有理由不使用重复的“执行”而不是直接编译来构建网络?
  • @Philonous 您能否更精确地添加显示 dbus 和您的代码之间交互的代码?我认为阅读行为很有可能与事件打包在一起。
  • @Philonous 对于第二个问题:你能在 Github 上为此提出问题吗?我确实看到了在网络建成后运行 MomentIO 的必要性。请注意,如果允许多个网络,那么从sync 返回一个行为是有问题的,因为您可以尝试在另一个网络中使用它,这将非常失败。
  • @erik-allik Reactive-banana 确实已经有样本(称为valueB),但缺少的是sync。我设法使用响应式香蕉中可用的原语simulate 它,因此它不会违反响应式香蕉尚未违反的任何法律。

标签: haskell io frp reactive-banana sodiumfrp


【解决方案1】:

如果您有一个 Behavior 对您的属性值进行建模,并且您有一个 Event 对属性值的传入请求进行建模,那么您可以使用 (<@) :: Behavior b -> Event a -> Event b1 获取新事件在您传入请求时发生,并具有当时属性的值)。然后,您可以将其转换为回复请求所需的实际 IO 操作,并照常使用reactimate


1https://hackage.haskell.org/package/reactive-banana-1.1.0.0/docs/Reactive-Banana-Combinators.html#v:-60--64-

【讨论】:

  • 没有响应请求的动作,而是发送为 dbus 属性设置的回调的返回值作为响应。我可以做的是创建一个新的 IORef 并与请求一起传递到事件中,使用 reactimate 将采样值放入 IORef,然后在事件调用返回后在请​​求处理函数中读取 IORef。但是,与钠提供的简单解决方案相比,这需要在两个地方(事件网络和回调函数)交互代码,这使得代码更难维护。
【解决方案2】:

出于概念/架构的原因,Reactive Banana 具有从 EventBehavior 的功能,但反之则不然,考虑到 FRP 的性质和含义,它也很有意义。我很确定您可以编写一个轮询函数,但您应该考虑更改底层代码以公开事件。

您是否有不能将Behavior 更改为Event 的原因?如果没有,那将是解决您的问题的好方法。 (从理论上讲,它甚至可能揭示您迄今为止一直忽略的设计缺陷。)

【讨论】:

  • 我不确定这如何适用于我的问题。我相当肯定它应该是一种行为,因为它模拟了一个随时间变化的值。我的问题是我有一个来自网络的请求,我想用这个行为的当前值来回答。当然,我可以将这个请求的到达建模为一个事件(通过触发一个处理程序),但这只会让请求进入(frp-)网络,它无法检索结果发送回。
  • 我对“民意调查”一词的选择可能具有误导性。我不想检查值何时发生变化,而是想在特定时间检索 Behavior 的值,例如回答网络请求(想想 HTTP)
  • 哦,我明白了,那是我的错 :)
  • 实际上撤回了我的删除 - 我仍然认为这是要走的路。仅当存在 no 底层 Event 即“触发”对 Behavior 的更改时,这才不起作用,这可能会或可能不会发生。但话又说回来,您也可以考虑以依赖传入事件来发出传出事件的方式更改您的设计。其他任何东西要么是 hack(违反法律),要么是近似值(基于时间的采样)......但我可能会遗漏一些东西。
【解决方案3】:

答案似乎是“有可能”。

sample 对应于valueB,但没有直接等价于sync

不过,可以在execute的帮助下重新实现:

module Sync where

import Control.Monad.Trans
import Data.IORef
import Reactive.Banana
import Reactive.Banana.Frameworks

data Network = Network { eventNetwork :: EventNetwork
                       , run :: MomentIO () -> IO ()
                       }

newNet :: IO Network
newNet = do
    -- Create a new Event to handle MomentIO actions to be executed
    (ah, call) <- newAddHandler
    network <- compile $ do
        globalExecuteEV <- fromAddHandler ah
        -- Set it up so it executes MomentIO actions passed to it
        _ <- execute globalExecuteEV
        return ()
    actuate network
    return $ Network { eventNetwork = network
                     , run = call -- IO Action to fire the event
                     }

-- To run a MomentIO action within the context of the network, pass it to the
-- event.
sync :: Network -> MomentIO a -> IO a
sync Network{run = call} f = do
    -- To retrieve the result of the action we set up an IORef
    ref <- newIORef (error "Network hasn't written result to ref")
    -- (`call' passes the do-block to the event)
    call $ do
        res <- f
        -- Put the result into the IORef
        liftIO $ writeIORef ref res
    -- and read it back once the event has finished firing
    readIORef ref

-- Example
main :: IO ()
main = do
    net <- newNet -- Create an empty network
    (bhv1, set1) <- sync net $ newBehavior (0 :: Integer)
    (bhv2, set2) <- sync net $ newBehavior (0 :: Integer)
    set1 3
    set2 7
    let sumB = (liftA2 (+) bhv1 bhv2)
    print =<< sync net (valueB sumB)
    set1 5
    print =<< sync net (valueB sumB)
    return ()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    • 1970-01-01
    • 2012-03-31
    • 2016-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多