从外部网络采样行为
Sampling a behaviour from outside network
由于作者已 deprecated 钠,我正在尝试将我的代码移植到 reactive-banana。但是,两者之间似乎存在一些不一致之处,我很难克服。
例如,在钠中很容易检索行为的当前值:
retrieve :: Behaviour a -> IO a
retrieve b = sync $ sample b
我不知道如何在 reactive-banana 中做到这一点
(我想要这个的原因是因为我试图将行为导出为 dbus 属性。可以从其他 dbus 客户端查询属性)
编辑:替换了 "poll" 一词,因为它具有误导性
出于 conceptual/architectural 的原因,Reactive Banana 具有从 Event
到 Behavior
的功能,但反之则不然,考虑到 FRP 的性质和意义,它也是有意义的。我很确定您可以编写轮询函数,但您应该考虑更改底层代码以公开事件。
你有什么理由不能把你的 Behavior
变成 Event
吗?如果没有,那将是解决您的问题的好方法。 (理论上它甚至可能揭示您迄今为止一直忽视的设计缺陷。)
如果您有一个对 属性 的值进行建模的行为,并且有一个对 属性 的值的传入请求进行建模的事件,那么您可以只使用 (<@) :: Behavior b -> Event a -> Event b
1 以获取在传入请求时发生的新事件,其值为 属性 当时的值)。然后您可以将其转换为您需要采取的实际 IO 操作来回复请求并像往常一样使用 reactimate
。
答案似乎是"it's sort of possible"。
sample corresponds to valueB, but there is no direct equivalent to sync。
不过可以re-implemented借助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 ()
由于作者已 deprecated 钠,我正在尝试将我的代码移植到 reactive-banana。但是,两者之间似乎存在一些不一致之处,我很难克服。
例如,在钠中很容易检索行为的当前值:
retrieve :: Behaviour a -> IO a
retrieve b = sync $ sample b
我不知道如何在 reactive-banana 中做到这一点
(我想要这个的原因是因为我试图将行为导出为 dbus 属性。可以从其他 dbus 客户端查询属性)
编辑:替换了 "poll" 一词,因为它具有误导性
出于 conceptual/architectural 的原因,Reactive Banana 具有从 Event
到 Behavior
的功能,但反之则不然,考虑到 FRP 的性质和意义,它也是有意义的。我很确定您可以编写轮询函数,但您应该考虑更改底层代码以公开事件。
你有什么理由不能把你的 Behavior
变成 Event
吗?如果没有,那将是解决您的问题的好方法。 (理论上它甚至可能揭示您迄今为止一直忽视的设计缺陷。)
如果您有一个对 属性 的值进行建模的行为,并且有一个对 属性 的值的传入请求进行建模的事件,那么您可以只使用 (<@) :: Behavior b -> Event a -> Event b
1 以获取在传入请求时发生的新事件,其值为 属性 当时的值)。然后您可以将其转换为您需要采取的实际 IO 操作来回复请求并像往常一样使用 reactimate
。
答案似乎是"it's sort of possible"。
sample corresponds to valueB, but there is no direct equivalent to sync。
不过可以re-implemented借助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 ()