Haskell Language
Banane réactive
Recherche…
Injection d'événements externes dans la bibliothèque
Cet exemple n'est lié à aucune boîte à outils graphique, comme le fait réactif-banana-wx, par exemple. Au lieu de cela, il montre comment injecter des actions IO
arbitraires dans les machines FRP.
Le module Control.Event.Handler
fournit une fonction addHandler
qui crée une paire de valeurs AddHandler a
et a -> IO ()
. Le premier est utilisé par reactive-banana lui-même pour obtenir un Event a
, alors que le second est une fonction simple utilisée pour déclencher l'événement correspondant.
import Data.Char (toUpper) import Control.Event.Handler import Reactive.Banana main = do (inputHandler, inputFire) <- newAddHandler
Dans notre cas a
paramètre du gestionnaire est de type String
, mais le code qui permet de déduire du compilateur qui sera écrit plus tard.
Nous définissons maintenant le EventNetwork
qui décrit notre système FRP. Ceci est fait en utilisant la fonction de compile
:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler
La fonction fromAddHandler
transforme AddHandler a
valeur Event a
, qui est traitée dans l'exemple suivant.
Enfin, nous lançons notre "boucle d'événements", qui déclencherait des événements sur la saisie de l'utilisateur:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... forever $ do input <- getLine inputFire input
Type d'événement
En mode réactif-banane, le type d' Event
représente un flux de certains événements dans le temps. Un Event
est similaire à un signal d'impulsion analogique dans le sens où il n'est pas continu dans le temps. Par conséquent, Event
est une instance de la Functor
types Functor uniquement. Vous ne pouvez pas combiner deux Event
ensemble car ils peuvent tirer à des moments différents. Vous pouvez faire quelque chose avec la valeur [actuelle] d'un Event
et y réagir avec une action IO
.
Les transformations sur la valeur de l' Event
sont effectuées en utilisant 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
Réagir à un Event
se fait de la même manière. D'abord, vous le fmap
avec une action de type a -> IO ()
et vous la transmettez ensuite à la fonction reactimate
:
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
Maintenant, chaque fois que inputFire "something"
est appelé "SOMETHING"
, "SOMETHING"
sera imprimé.
Type de comportement
Pour représenter des signaux continus, les caractéristiques réactives-bananes Behavior a
type. Contrairement à Event
, un Behavior
est un Applicative
, qui vous permet de combiner n Behavior
aide d'une fonction pure n-aire (en utilisant <$>
et <*>
).
Pour obtenir un Behavior a
partir de l' Event a
il existe accumE
fonction d' accumE
:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
accumE
prend la valeur initiale de Behavior
et un Event
, contenant une fonction qui lui donnerait la nouvelle valeur.
Comme avec Event
s, vous pouvez utiliser fmap
pour travailler avec la valeur actuelle du Behavior
, mais vous pouvez également les combiner avec (<*>)
.
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'
Pour réagir aux changements de Behavior
, il y a une fonction 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
La seule chose à noter est que les changes
renvoient l' Event (Future a)
au lieu de l' Event a
. À cause de cela, reactimate'
doit être utilisé au lieu de reactimate
. La justification derrière cela peut être obtenue de la documentation.
Activer les réseaux d'événements
EventNetwork
retournés par compile
doivent être activés avant que les événements réactivés aient un effet.
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!"