Haskell Language
Plátano reactivo
Buscar..
Inyectando eventos externos en la biblioteca.
Este ejemplo no está vinculado a ningún conjunto de herramientas GUI concreto, como hace el caso de reactivo-banana-wx, por ejemplo. En su lugar, muestra cómo inyectar acciones arbitrarias de IO
/ IO
en la maquinaria de FRP.
El módulo Control.Event.Handler
proporciona una función addHandler
que crea un par de AddHandler a
y a -> IO ()
. El primero es utilizado por el propio banana reactiva para obtener un Event a
valor, mientras que el segundo es una función simple que se usa para desencadenar el evento correspondiente.
import Data.Char (toUpper) import Control.Event.Handler import Reactive.Banana main = do (inputHandler, inputFire) <- newAddHandler
En nuestro caso el a
parámetro del controlador es de tipo String
, pero el código que permite inferir que el compilador se escribirá más tarde.
Ahora definimos la EventNetwork
que describe nuestro sistema controlado por FRP. Esto se hace usando la función de compile
:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler
La función fromAddHandler
transforma AddHandler a
valor AddHandler a
en un Event a
, que se trata en el siguiente ejemplo.
Finalmente, lanzamos nuestro "bucle de eventos", que dispararía eventos en la entrada del usuario:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... forever $ do input <- getLine inputFire input
Tipo de evento
En reactive-banana, el tipo de Event
representa una secuencia de algunos eventos en el tiempo. Un Event
es similar a una señal de impulso analógica en el sentido de que no es continua en el tiempo. Como resultado, Event
es una instancia de la clase de tipos de Functor
solamente. No puedes combinar dos Event
s juntos porque pueden disparar en diferentes momentos. Puede hacer algo con el valor [actual] de un Event
y reaccionar a él con alguna acción IO
.
Las transformaciones en el valor del Event
se realizan usando fmap
:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler -- turn all characters in the signal to upper case let inputEvent' = fmap (map toUpper) inputEvent
Reaccionar ante un Event
se hace de la misma manera. Primero fmap
con una acción de tipo a -> IO ()
y luego reactimate
para reactimate
función:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler -- turn all characters in the signal to upper case let inputEvent' = fmap (map toUpper) inputEvent let inputEventReaction = fmap putStrLn inputEvent' -- this has type `Event (IO ()) reactimate inputEventReaction
Ahora, cada vez que se inputFire "something"
, se imprimirá "SOMETHING"
.
Tipo de comportamiento
Para representar señales continuas, características reactivas de banana Behavior a
tipo. A diferencia del Event
, un Behavior
es un Applicative
, que le permite combinar n Behavior
usando una función pura n-aría (usando <$>
y <*>
).
Para obtener un Behavior a
del Event a
hay Event a
función de accumE
:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
accumE
toma el valor inicial de Behavior
y un Event
, que contiene una función que lo establecería en el nuevo valor.
Al igual que con los Event
s, puede usar fmap
para trabajar con el valor del Behavior
actual, pero también puede combinarlos con (<*>)
.
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent inputBehavior' <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent let constantTrueBehavior = (==) <$> inputBehavior <*> inputBehavior'
Para reaccionar ante los cambios de Behavior
hay una función de changes
:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent inputBehavior' <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent let constantTrueBehavior = (==) <$> inputBehavior <*> inputBehavior' inputChanged <- changes inputBehavior
Lo único que se debe tener en cuenta es que los changes
devuelven el Event (Future a)
lugar del Event a
. Debido a esto, reactimate'
se debe utilizar en lugar de reactimate
. La razón detrás de esto se puede obtener de la documentación.
Actuating EventNetworks
EventNetwork
devueltos por compile
deben activarse antes de que los eventos reactivados tengan un efecto.
main = do
(inputHandler, inputFire) <- newAddHandler
eventNetwork <- compile $ do
inputEvent <- fromAddHandler inputHandler
let inputEventReaction = fmap putStrLn inputEvent
reactimate inputEventReaction
inputFire "This will NOT be printed to the console!"
actuate eventNetwork
inputFire "This WILL be printed to the console!"