In the last post I created a simple echo program using just events. In this post I introduce behaviors which are time varying values.
Let’s get the boilerplate out of the way
> import Reactive.Banana
> import Control.Monad
> import Data.Monoid
> eventLoop fire = loop
> loop = do
> s <- getLine
> fire s
> when (s /= “quit”) loop
> main = do
> (addHandler,fire) <- newAddHandler
> compile (networkDescription $ fromAddHandler addHandler ) >>= actuate
> eventLoop fire
> networkDescription lineEnd = do
> let run p f = lineEnd >>= \a -> reactimate $ fmap f (filterE p a)
> run (/=”quit”) $ putStrLn . (“You typed: “++)
> run (==”quit”) $ const (putStrLn “Goodbye”)
Let’s make a series of behaviours, starting with a behaviour that is constantly 1:
> let oneB :: Behavior Integer
> oneB = pure 1
Displaying a behaviors value took a little thinking to figure out. Behaviors are continous functions of time, so there is no function to transform one to an event.
What we can do is apply a function in a behavior to the value in an event.
> let bToE :: Behavior a -> Event b -> Event a
> bToE b = apply (const <$> b)
> oneE = bToE oneB
Then we can output the transformed event.
> let outpB e b f = e >>= \a -> reactimate $ fmap f (bToE b a)
> outpB lineEnd oneB $ putStrLn . (“oneB: ” ++) . show
We can create a behavior that changes values to an event’s value when it fires with the stepper function.
> lineE <- lineEnd
> let lineB = stepper “” lineE
> outpB lineEnd lineB $ putStrLn . (“lineB: ” ++) . show
Events form a monoid that we can use to create a behavior that changes value with either of two events.
> let ynB = stepper “N” $ filterE (==”Y”) lineE
> `mappend` filterE (==”N”) lineE
> outpB lineEnd ynB $ putStrLn . (“ynB: ” ++) . show
We can also create behaviors that fold a function valued event. This is good for accumulating updates.
For example, an increment event can be used to get a count of commands given
> let incE = fmap (const (+1)) lineE
> countB = accumB 0 incE
> outpB lineEnd countB $ putStrLn . (“countB: ” ++) . show
We are not limitted to single updates
> let undoE = fmap (const (subtract 2)) $ filterE (==”undo”) lineE
> realCountB = accumB 0 $ incE `mappend` undoE
> outpB lineEnd realCountB $ putStrLn . (“realCountB: ” ++) . show
This last example shows the promise of FRP. We can specify a complex behavior that involves multiple events declaratively and in one place.