Выборка поведения из внешней сети
Так как натрий был объявлен устаревшим автором, я пытаюсь перенести свой код на реактив-банан. Однако, между этими двумя, кажется, есть некоторые несоответствия, которые я с трудом преодолеваю.
Например, в натрии было легко получить текущее значение поведения:
retrieve :: Behaviour a -> IO a
retrieve b = sync $ sample b
Я не вижу, как это сделать в реактивном банане
(Причина, по которой я этого хочу, заключается в том, что я пытаюсь экспортировать поведение как свойство dbus. Свойства можно запрашивать у других клиентов dbus)
Редактировать: заменил слово "опрос", поскольку это вводило в заблуждение
3 ответа
Ответ, кажется, "это вроде возможно".
sample соответствует значению B, но прямого эквивалента для синхронизации нет.
Однако, это может быть повторно реализовано с помощью 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 ()
Если у вас есть Поведение, моделирующее значение вашего свойства, и у вас есть Событие, моделирующее входящие запросы на значение свойства, то вы можете просто использовать (<@) :: Behavior b -> Event a -> Event b
1, чтобы получить новое событие, происходящее во время ваших входящих запросов со значением, которое свойство имеет в то время). Затем вы можете преобразовать это в фактические действия ввода-вывода, которые вам нужно предпринять, чтобы ответить на запрос и использовать reactimate
по-прежнему.
1 https://hackage.haskell.org/package/reactive-banana-1.1.0.0/docs/Reactive-Banana-Combinators.html
По концептуальным / архитектурным причинам Reactive Banana имеет функции от Event
в Behavior
, но не наоборот, и это тоже имеет смысл, учитывая природу и значение FRP. Я вполне уверен, что вы можете написать функцию опроса, но вместо этого вы должны рассмотреть возможность изменения базового кода, чтобы вместо этого показывать события.
Есть ли причина, по которой вы не можете изменить Behavior
в Event
? Если нет, то это был бы хороший способ решить вашу проблему. (Теоретически это может даже выявить недостаток дизайна, который вы до сих пор пропускали.)