Ricerca…


Iniezione di eventi esterni nella libreria

Questo esempio non è legato ad alcun concreto toolkit GUI, come ad esempio reactive-banana-wx. Invece mostra come iniettare azioni IO arbitrarie nei macchinari FRP.

Il modulo Control.Event.Handler fornisce una funzione addHandler che crea una coppia di valori AddHandler a addHandler a -> IO () . Il primo è usato dalla banana reattiva per ottenere un valore di Event a , mentre il secondo è una funzione semplice che viene utilizzata per attivare l'evento corrispondente.

import Data.Char (toUpper)

import Control.Event.Handler
import Reactive.Banana

main = do
    (inputHandler, inputFire) <- newAddHandler

Nel nostro caso l' a parametro del gestore è di tipo String , ma il codice che permette compilatore dedurre che sarà scritto in seguito.

Ora definiamo EventNetwork che descrive il nostro sistema basato su FRP. Questo viene fatto usando la funzione di compile :

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        inputEvent <- fromAddHandler inputHandler

La funzione fromAddHandler trasforma AddHandler a valore in un Event a , che è coperto nel prossimo esempio.

Infine, lanciamo il nostro "ciclo degli eventi", che genererebbe eventi in entrata dell'utente:

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        ...
    forever $ do
        input <- getLine
        inputFire input

Tipo di evento

In banana reattiva il tipo di Event rappresenta un flusso di alcuni eventi nel tempo. Un Event è simile a un segnale di impulso analogico nel senso che non è continuo nel tempo. Di conseguenza, Event è solo un'istanza della Functor di Functor Functor. Non è possibile combinare due Event insieme perché potrebbero sparare in momenti diversi. Puoi fare qualcosa con il valore [corrente] di un Event e reagire ad esso con qualche azione IO .

Le trasformazioni sul valore Event vengono eseguite 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

La reazione a un Event viene eseguita allo stesso modo. Per prima cosa si fmap con un'azione di tipo a -> IO () e quindi passarlo alla funzione 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

Ora ogni volta che viene inputFire "something" viene stampato "SOMETHING" .

Tipo di comportamento

Per rappresentare segnali continui, le caratteristiche di banana reattiva Behavior a tipo. Diversamente Event , un Behavior è un Applicative , che ti consente di combinare n Behavior usando una funzione pura n-ario (usando <$> e <*> ).

Per ottenere un Behavior a Event a c'è la funzione accumE :

main = do
    (inputHandler, inputFire) <- newAddHandler
    compile $ do
        ...
        inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent

accumE prende il valore iniziale di Behavior e un Event , contenente una funzione che lo imposterà sul nuovo valore.

Come per gli Event s, puoi usare fmap per lavorare con il valore del Behavior corrente, ma puoi anche combinarli 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'

Per reagire alle modifiche del Behavior , esiste una funzione di 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

L'unica cosa da notare è che le changes restituiscono Event (Future a) invece di Event a . Per questo reactimate' dovrebbe essere usato un tempo di reactimate invece di un reactimate di reactimate . La logica alla base di questo può essere ottenuta dalla documentazione.

Attuando EventNetworks

EventNetwork restituito da compile deve essere attivato prima che gli eventi reattivi abbiano un effetto.

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


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow