Haskell Language
Reactieve-banaan
Zoeken…
Externe gebeurtenissen in de bibliotheek injecteren
Dit voorbeeld is niet gekoppeld aan een concrete GUI-toolkit, zoals reactieve-banana-wx bijvoorbeeld. In plaats daarvan wordt getoond hoe willekeurige IO
acties in FRP-machines kunnen worden geïnjecteerd.
De Control.Event.Handler
module geeft een addHandler
functie die een paar creëert AddHandler a
en a -> IO ()
waarden. De eerste wordt gebruikt door reactieve-banaan zelf om een Event a
waarde te verkrijgen, terwijl de laatste een eenvoudige functie is die wordt gebruikt om de overeenkomstige gebeurtenis te activeren.
import Data.Char (toUpper) import Control.Event.Handler import Reactive.Banana main = do (inputHandler, inputFire) <- newAddHandler
In ons geval het a
parameter van de handler van het type String
, maar de code waarmee compiler afleiden dat later zal worden geschreven.
Nu definiëren we het EventNetwork
dat ons FRP-aangedreven systeem beschrijft. Dit wordt gedaan met behulp van de compile
:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler
De functie fromAddHandler
transformeert AddHandler a
waarde in een Event a
, die in het volgende voorbeeld wordt behandeld.
Ten slotte lanceren we onze "event loop", die gebeurtenissen op gebruikersinvoer zou activeren:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... forever $ do input <- getLine inputFire input
Soort evenement
In reactieve banaan vertegenwoordigt het type Event
een stroom van sommige gebeurtenissen in de tijd. Een Event
is vergelijkbaar met een analoog impulssignaal in die zin dat het niet continu in de tijd is. Hierdoor is Event
alleen een instantie van de Functor
typeclass. Je kunt twee Event
niet samenvoegen omdat ze op verschillende tijdstippen kunnen vuren. Je kunt iets doen met de [huidige] waarde van een Event
en daarop reageren met een IO
actie.
Transformaties op Event
's waarde worden gedaan met behulp van 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
Reageren op een Event
gebeurt op dezelfde manier. Eerst fmap
het met een actie van type a -> IO ()
en geef je het vervolgens door aan de reactimate
functie:
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
Wanneer inputFire "something"
wordt genoemd, wordt "SOMETHING"
afgedrukt.
Gedragstype
Om continue signalen weer te geven, heeft reactief-banaan een Behavior a
. In tegenstelling tot Event
is een Behavior
een Applicative
, waarmee u n Behavior
kunt combineren met behulp van een pure functie (met <$>
en <*>
).
Om een Behavior a
te verkrijgen van de Event a
er een accumE
functie:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
accumE
neemt de initiële waarde van Behavior
en een Event
, met een functie die deze op de nieuwe waarde zou zetten.
Net als bij Event
s, kunt u fmap
om te werken met de huidige waarde van Behavior
, maar u kunt ze ook combineren met (<*>)
.
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'
Om te reageren op Behavior
is er een 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
Het enige dat moet worden opgemerkt, is dat changes
Event (Future a)
plaats van Event a
retourneren. Daarom moet reactimate'
worden gebruikt in plaats van reactimate
. De reden hiervoor is te vinden in de documentatie.
Activeren van EventNetworks
EventNetwork
's geretourneerd door compile
moeten worden geactiveerd voordat gereactiveerde gebeurtenissen effect hebben.
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!"