Выборка поведения из внешней сети

Так как натрий был объявлен устаревшим автором, я пытаюсь перенести свой код на реактив-банан. Однако, между этими двумя, кажется, есть некоторые несоответствия, которые я с трудом преодолеваю.

Например, в натрии было легко получить текущее значение поведения:

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 b1, чтобы получить новое событие, происходящее во время ваших входящих запросов со значением, которое свойство имеет в то время). Затем вы можете преобразовать это в фактические действия ввода-вывода, которые вам нужно предпринять, чтобы ответить на запрос и использовать 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? Если нет, то это был бы хороший способ решить вашу проблему. (Теоретически это может даже выявить недостаток дизайна, который вы до сих пор пропускали.)

Другие вопросы по тегам