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