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.

Advertisements

March 25, 2012

Trying to figure out FRP

Filed under: Haskell — Tags: , , — llayland @ 10:12 pm

This is my attempt at learning FRP and sharing as I go.

I’ll be using Reactive-Banana as it looks simple and seems to have the most activity.

> import Reactive.Banana
> import Control.Monad

To keep things simple I will forgo a gui library. A console application is not the usual target for FRP programs, but it allows me to focus on the logic instead of the framework.
So, we need an event loop for the console that will allow us to fire events.

> eventLoop fire = loop
>    where
>    loop = do
>       s <- getLine
>       fire s
>       when (s /= “quit”) loop

A non FRP implementation of our program is very simple; simply make the fire function generate an IO action:

> echoNonFRP = eventLoop $ putStrLn . (“You typed: “++)

In this trivial example, it makes sense to handle the input right here. FRP is definitely overkill, but I will implement an FRP solution to show how it allows us to seperate the firing of an event from response to the event.

> echoFRP = do

first we use the newAddHandler action to generate a pair of a handler and a firing function. These are linked together somehow so that the handler can be used to recognize events that are created by the firing function

>   (addHandler,fire) <- newAddHandler

we can use the addhandler to build a network description. We’ll flesh out the details of the description later.  For now, we just compile it into a nework and then actuate the network to turn it into an IO action.

>   compile (fromAddHandler addHandler >>= networkDescription’) >>= actuate

The type of fire (a -> IO ()) is exactly what we need to pass into our eventLoop.

>   eventLoop fire

Now we just need to fill in our network description. The reactimate function is used to emit the value of an event when it occurs. We need an IO action instead of a String, so we map a function (a -> IO) over the event.

> networkDescription’ lineE = reactimate $ fmap (putStrLn . (“You typed: “++)) lineE

and that is it. This functions identically to the non FRP version.

here it is without interuption and special handling for the quit command:

> networkDescription lineE = do
>   let run p f = lineE >>= \a -> reactimate $ fmap f (filterE p a)
>   run (/=”quit”) $ putStrLn . (“You typed: “++)
>   run (==”quit”) $ const (putStrLn “Goodbye”)

> echo = do
>   (addHandler,fire) <- newAddHandler
>   compile (networkDescription $ fromAddHandler addHandler ) >>= actuate
>   eventLoop fire

Create a free website or blog at WordPress.com.