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!"