Llayland’s Food, Oracle, and Haskell

March 28, 2012

Trying to figure out FRP 2 (Behaviors)

Filed under: Haskell — Tags: , , — llayland @ 3:32 am

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
>    where
>    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.

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Create a free website or blog at WordPress.com.