Szukaj…


Wstrzykiwanie zdarzeń zewnętrznych do biblioteki

Ten przykład nie jest powiązany z żadnym konkretnym zestawem GUI, jak na przykład reactive-banana-wx. Zamiast tego pokazuje, jak wstrzyknąć arbitralne działania IO do maszyn FRP.

Moduł Control.Event.Handler udostępnia funkcję addHandler która tworzy parę wartości AddHandler a oraz a -> IO () . Pierwszy z nich jest używany przez samego banana reaktywnego do uzyskania wartości Event a , a drugi jest zwykłą funkcją, która jest używana do wyzwolenia odpowiedniego zdarzenia.

import Data.Char (toUpper)

import Control.Event.Handler
import Reactive.Banana

main = do
    (inputHandler, inputFire) <- newAddHandler

W naszym przypadku parametrem obsługi jest typu a String , ale kod, który pozwala wnioskować, że kompilator będzie napisany później.

Teraz definiujemy EventNetwork który opisuje nasz system oparty na FRP. Odbywa się to za pomocą funkcji compile :

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

Funkcja fromAddHandler przekształca wartość AddHandler a w Event a , które AddHandler a w następnym przykładzie.

Wreszcie uruchamiamy naszą „pętlę zdarzeń”, która uruchamiałaby zdarzenia na podstawie danych wprowadzonych przez użytkownika:

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

Typ wydarzenia

W reactive-banana typ Event reprezentuje strumień niektórych zdarzeń w czasie. Event jest podobne do analogowego sygnału impulsowego w tym sensie, że nie jest ciągłe w czasie. W wyniku tego Event jest wystąpienie z Functor tylko typeclass. Nie możesz łączyć dwóch Event ponieważ mogą strzelać w różnych momentach. Możesz zrobić coś z wartością [bieżącą] Event i zareagować na to za pomocą akcji IO .

Transformacje wartości Event są wykonywane za pomocą 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

Reakcja na Event odbywa się w ten sam sposób. Najpierw fmap go za pomocą akcji typu a -> IO () a następnie przekaż go do reactimate funkcji:

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

Teraz za każdym razem, gdy inputFire "something" , drukowane będzie "SOMETHING" .

Rodzaj zachowania

Aby reprezentować ciągłe sygnały, reaktywne banany cechują się Behavior a typu. W przeciwieństwie do Event , Behavior jest Applicative , która pozwala łączyć n Behavior za pomocą n-ary czystej funkcji (używając <$> i <*> ).

Aby uzyskać Behavior a ze Event a dostępna jest funkcja accumE :

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

accumE przyjmuje wartość początkową Behavior i Event , zawierające funkcję, która accumE jej nową wartość.

Podobnie jak w przypadku Event , możesz użyć fmap do pracy z bieżącą wartością Behavior , ale możesz także połączyć je z (<*>) .

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'

Aby zareagować na zmiany w Behavior dostępna jest funkcja 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

Jedyną rzeczą, na którą należy zwrócić uwagę, jest to, że changes zwracają Event (Future a) zamiast Event a . Z tego reactimate' należy stosować zamiast reactimate . Uzasadnienie tego można znaleźć w dokumentacji.

Uruchamianie EventNetworks

EventNetwork zwrócony przez compile musi zostać aktywowany, zanim ponownie zareagowane zdarzenia zaczną działać.

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow