Haskell Language
Reaktive Banane
Suche…
Einfügen externer Ereignisse in die Bibliothek
Dieses Beispiel ist nicht an ein konkretes GUI-Toolkit gebunden, wie es zum Beispiel die reaktive Banane-wx ist. Stattdessen zeigt es , wie willkürliche einzuspritzen IO
Aktionen in FRP Maschinen.
Das Control.Event.Handler
Modul stellt eine addHandler
Funktion addHandler
, die ein Paar von AddHandler a
und a -> IO ()
. Ersteres wird von der reaktiven Banane selbst verwendet, um einen Event a
Wert zu erhalten, während letzteres eine einfache Funktion ist, die zum Auslösen des entsprechenden Events verwendet wird.
import Data.Char (toUpper) import Control.Event.Handler import Reactive.Banana main = do (inputHandler, inputFire) <- newAddHandler
In unserem Fall ist die a
Parameter des Handlers ist vom Typ String
, aber der Code, den Compiler schließen läßt , die später geschrieben werden.
Nun definieren wir das EventNetwork
, das unser FRP-gesteuertes System beschreibt. Dies geschieht mit der compile
Funktion:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler
Die Funktion fromAddHandler
wandelt AddHandler a
in ein Event a
, das im nächsten Beispiel behandelt wird.
Schließlich starten wir unsere "Ereignisschleife", die auf Benutzereingaben Ereignisse auslöst:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... forever $ do input <- getLine inputFire input
Ereignistyp
In reaktiver Banane der Event
stellt Art einen Strom von einigen Ereignissen in der Zeit. Ein Event
ähnelt einem analogen Impulssignal in dem Sinne, dass es zeitlich nicht stetig ist. Daher ist Event
nur eine Instanz der Functor
. Sie können nicht zwei Event
miteinander kombinieren, da sie zu unterschiedlichen Zeiten ausgelöst werden. Sie können etwas mit dem [aktuellen] Wert eines Event
und darauf mit einer IO
Aktion reagieren.
Transformationen für den Wert von Event
werden mit 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
Auf ein Event
reagieren erfolgt auf dieselbe Weise. Zuerst fmap
Sie es mit einer Aktion vom Typ a -> IO ()
und übergeben es dann an die reactimate
Funktion:
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
Immer wenn inputFire "something"
genannt wird, wird "SOMETHING"
gedruckt.
Verhaltenstyp
Um kontinuierliche Signale darzustellen, verhalten sich reaktive Bananenmerkmale Behavior a
Typ. Im Gegensatz zu Event
ist ein Behavior
ein Applicative
, mit dem Sie n Behavior
mit einer n-ary pure-Funktion (mit <$>
und <*>
) kombinieren können.
Um ein Behavior a
vom Event a
gibt es accumE
Funktion accumE
:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
accumE
übernimmt den Anfangswert von Behavior
und ein Event
, das eine Funktion enthält, die den neuen Wert festlegen würde.
Wie bei Event
können Sie fmap
, um mit dem aktuellen Behavior
zu arbeiten. Sie können sie jedoch auch mit (<*>)
kombinieren.
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'
Um auf Behavior
zu reagieren, gibt es eine 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
Das einzige, was beachtet werden sollte, ist, dass changes
Event (Future a)
statt Event a
. Aus diesem reactimate'
sollte reactimate
anstelle von reactimate
. Die Gründe dafür können der Dokumentation entnommen werden.
EventNetworks aktivieren
EventNetwork
von compile
zurückgegeben werden compile
müssen aktiviert werden, bevor reaktivierte Ereignisse wirken.
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!"