Functional Reactive Programming kick-starter guide


Recently I’ve been wondering about making a simple game in Haskell (one of many attempt to realize something playable). I’ve been caught by Game Entity System, a sort of Architectural Pattern to develop game logic (read an excellent introduction here.) Nevertheless, before even starting to think how to apply it in a functional context, I’ve read another article where the emphasis was focused on Functional Reactive Programming. So I thought : Ok, I’ve only heard about FRP, but what’s all this jabber about? And more important, how it can be useful to me?

So I decided to wrap my head around it. This is a sort of preliminary diary to FRP of “The layman’s guide to FRP in practice”. I hope this will be useful to someone, or at least to me to better grasp FRP!

FRP in Haskell

If you have heard of FRP before, probably was because you are an Haskell programmer /enthusiast, because FRP seems (and I repeat, seems, don’t want to offend nobody) to be born around the Haskell Community (see Fran Papers). So the first step to understand something is to have a documentation for it. Now, this is a tricky part, because since FRP is so entangled with Academic environment, most documentation is mathematically biased. So I began my search for an usable library of something that doesn’t require you to be a Math PhD. I’ve tried a few libraries, but in the end I stick with reactive-banana: still in active development, funny name, good documentation and a tons of examples. So I started to dig inside.

Functional Reactive Programming in a nutshell

Now, don’t try to fully understand everything I will tell you, and the same applies for the code: some passages are still dark even for me. In first place I want to say thanks to the developer of Reactive Banana, Heinrich Apfelmus, for having provided the only library I can understand (I don’t want to blame Yampa or the other libraries, but this seemed to me the most straightforward to start with). Heinrich’s excellent introduction on FRP and Reactive Banana helped, too: I’ll take quotes from that introduction, when needed, so credits are due. Ah, a last thing! Reactive Programming comes in different flavours, and can be applied even in the imperative world, obviously. So the examples I’ll show you have the sole scope to  introduce you to FRP, nothing more.

A wallet

To get the gist of FRP just image a wallet, a simple leather wallet like this:

And now image a father and a son: the father, in elegant attire, and the son, in more modern clothes. Both posses a wallet, of course, but these wallet are bind by a mystical father-son rule:

The father will always have 10 dollars more than his son.

No matter what will happen, this will still remain the same. Now think about this with the eye of the imperative programmer. When an imperative programmer comes home, and work on his fantastic game “Father And Son Wallet War v. 0.0.1″, he would probably write something like this, for example, in Python:

son_wallet = Wallet(5)
father_wallet = Wallet(son_wallet.money + 10)

(Obviously this is a quick and dirty code, the sematic is real Python, but don’t expect to make it works). Now what happens when the son receives – let’s say – 50 dollars as birthday present? The imperative programmer has to update the code accordingly:

son_wallet.money += 50
father_wallet.money += 50

This is because the father, to be consistent with the father-son rules, has to update his wallet content too (let’s say with a part of his paycheck): in this way, the rule will be still valid (now the son has 55 bucks and the father 65, everything make sense). Have you seen what happened?  In the object declarations, we have expressed the father-son rule with that

son_wallet.money + 10

assignment, but one the objects have been initialized, we have lost this rule forever. While in the first snippet the father’s wallet value was dependent by the son’s wallet value, after the object initialization the two object have independent lives!  The state of one can change without affecting the other. Well, in most occasions this is the desirable behavior, but not this time, due our constrain.

Functional Reactive Programming at rescue

Now image a parallel world, in the galaxy of Functional Programming. Our functional programmer goes home, open his laptop and begin to work on “Father and Son Wallet War v 0.1 – Reactive Edition”. He thought to himself : “How can I make this program reactive to events? Well, let’s take a look to HackageDB… “, he download Reactive Banana and start reading documentation – “Ok, well, let’s see what Reactive Banana provides me..” and he discover the idea of time varying values.

“Ok!” he says “a Behavior is nothing but a value that changes over time! And why on earth a value must change? Oh, yes, it can changes after an Event!”. So our functional programmers has just discovered two important data type of RB (Reactive Banana), a Behavior and a Event. So he work hard all night long and produces this code, resulting from patchwork on several RB examples:

import Reactive.Banana as R

type Money = Int
type EventSource a = (AddHandler a, a -> IO ())

addHandler :: EventSource a -> AddHandler a
addHandler = fst

fire :: EventSource a -> a -> IO ()
fire = snd

main = do

  sources <- makeSources
  network <- setupNetwork sources
  actuate network
  fire (fst sources) ()
  fire (fst sources) ()
  fire (snd sources) ()

makeSources = (,) <$> newAddHandler <*> newAddHandler

setupNetwork (esputdollar, estakedollar) = compile $ do

  eup <- fromAddHandler (addHandler esputdollar)
  edown <- fromAddHandler (addHandler estakedollar)

  let

    -- Behavior : A VALUE that changes over time.
    -- A discrete is an observable behavior
    -- I'm saying that walletA is an accumulable value
    -- that starts from 0, and increase by 1 due to the
    -- eup event triggering, and decrease by 1 due to the
    -- edown event triggering.
    walletA :: Discrete Money
    walletA = accumD (0 :: Money) $
              ((+1) <$ eup)
              `union` (subtract 1 <$ edown)

    -- An event that is triggered when walletA changes.
    -- Allow us to "intercept" this changes and to peek
    -- inside the wallet.
    eWalletAUpdate :: Event Money
    eWalletAUpdate = changes walletA

    -- WalletB is stepper, something that starts from an
    -- initial value and that after a certain event changes
    -- in a new value.
    walletB :: Discrete Money
    walletB = stepperD (10 :: Money) $
              ((+10) <$> eWalletAUpdate)

    -- With this event I can peek inside walletB content.
    eWalletBUpdate :: Event Money
    eWalletBUpdate = changes walletB

  reactimate $ putStrLn . showDollarIncrease <$> eup
  reactimate $ putStrLn . showDollarDecrease <$> edown
  reactimate $ putStrLn . showWalletAContent <$> eWalletAUpdate
  reactimate $ putStrLn . showWalletBContent <$> eWalletBUpdate  

showDollarIncrease e = "Wallet's content increase by 1 dollar!"
showDollarDecrease e = "Wallet's content decrease by 1 dollar!"
showWalletAContent money = "Wallet's A content is now: " ++ show money
showWalletBContent money = "Wallet's B content is now: " ++ show money

He looks at the code and says “Uhm, is not straightforward like it seemed last night, let’s check it out again”. I will interpret the programmer thoughts, just trying to make them as clear as possible. So don’t panic and let’s decompose all this code with a top down approach. We want to create two values A and B, bind by a rule, i.e. value B = value A + 10, so we create two Discrete, that represent exactly this: values that change over time, and are observable. Ok, but when a value should change? It should changes when a Event occur. Let’s think about it, remembering what I’ve said to you before: what are two possible events in this simple world? Of course! The simplest events we can thing about are the event of “Adding a dollar” and the event “Taking a dollar”. But we have two more events, representing the wallet content change: sounds reasonable, isn’t it? So just recap: we have two wallet bind by a rule, and some events that can be triggered. This sounds just like an event-driven GUI programming, right? You may think events as a sort of GUI events or Javascript event (e.g. onClick, do something).

Finally understanding all this jabber

Ok, so let’s start decomposing it. Let’s start from the main:

main = do

  sources <- makeSources
  network <- setupNetwork sources
  actuate network
  fire (fst sources) ()
  fire (fst sources) ()
  fire (snd sources) ()

In order to trigger events, we need to create event sources, i.e. something where events born (think to them as monster spawing point. When we need to spawn a monster, it’s generated from THE source). In the same way, when we need to generate an event, we ask for it to the correct source. So now you understand why we have create the event sources with the makeSources function. Let’s move on. The next step, as you can see, is to create an EventNetwork that must express our rule. I haven’t found a better comparison for an EventNetwork than this: imagine your brain. Your brain is full of neurons, cells who are able to react to external stimulas. Neurons are pretty complex stuff, since they can react only to certains stimulas and are able to involve other neurons as well. Neural Networks are based on this simple behavior.

Why I have called our nervous system “Events highway”? Because it is, indeed! External stimulas (events) travel through your nervous system  into the brain. Here neurons can perform a sort of filtering to react only on events they are interested about. Now try to map this naive description of human body to our snippet:

setupNetwork (esputdollar, estakedollar) = compile $ do

  eup <- fromAddHandler (addHandler esputdollar)
  edown <- fromAddHandler (addHandler estakedollar)

  let

    walletA :: Discrete Money
    walletA = accumD (0 :: Money) $
              ((+1) <$ eup)
              `union` (subtract 1 <$ edown)

    eWalletAUpdate :: Event Money
    eWalletAUpdate = changes walletA

    walletB :: Discrete Money
    walletB = stepperD (10 :: Money) $
              ((+10) <$> eWalletAUpdate)

    eWalletBUpdate :: Event Money
    eWalletBUpdate = changes walletB

  reactimate $ putStrLn . showDollarIncrease  eup
  reactimate $ putStrLn . showDollarDecrease  edown
  reactimate $ putStrLn . showWalletAContent  eWalletAUpdate
  reactimate $ putStrLn . showWalletBContent  eWalletBUpdate

The first two lines simply “pop” the events from the sources, so there aren’t a big deal. Let’s focus on what happens inside the let.
We are saying: “Ok, built an event network with this features”:

  1. It can be traversed only by 4 events: eup, edown, ewalletAupdate, ewalletBupdate
  2. It has only two neurons: walletA and walletB

But we are saying more, though, we are specifying how walletA and walletB (our Father and Son wallets) are bind together:

  1. walletA (SON) is a wallet whose value increase by 1 after the event eup
  2. walletA (SON) is a wallet whose value decrease by 1 after the event edown
  3. walletA (SON) is a wallet whose initial value is 0
  4. walletB (Father) is a wallet which react to changes from A
  5. walletB (Father) is a wallet whose initial value is 10

Can you get it? Don’t worry, let’s start with the definition of walletA:

    walletA :: Discrete Money
    walletA = accumD (0 :: Money) $
              ((+1) <$ eup)
              `union` (subtract 1 <$ edown)

We are saying that walletA is an accumulator, a Behavior (a Discrete is nothing more than an observable Behavior) whose value increment over time. Yes, but how much and when? Well, it’s easy to see: its value increase by 1 on the event eup, and decrease by 1 on the event edown. union is a function used to concatenate streams of event. Pretty easy, isn’t it?
But we haven’t finished yet. We need an event that allows us to peek inside the wallet, just to print its value. So let’s write this event:

eWalletAUpdate :: Event Money
eWalletAUpdate = changes walletA

changes is a function that return an event when a Discrete changes, we definitely need it!

And what about our father? Well, let’s investigate:

    walletB :: Discrete Money
    walletB = stepperD (10 :: Money) $
              ((+10) <$> eWalletAUpdate)

Our walletB is always a Discrete, but of a different breed: it’s a stepper, a value that starts from 10 (in this case), and when a certain events is triggered, it update its value. This update is an overwrite, though, so we lose the previous value. But if you think about it, it’s what we want: we wanna update the previous value with the new one (took from our son’s wallet) and to add 10 dollar to enforce our rule: now it will be no mystery why we used the eWalletAUpdate as trigger.
Just like walletA, we need an event to peek inside the wallet:

eWalletBUpdate :: Event Money
eWalletBUpdate = changes walletB

Now let’s see what happens when the event eup enters our highway:

Sweet! The event eup caused our Father’s wallet to update this value as well! You can see what happened in this way: a car named eup arrived to our brain. This car stimulated the neuron walletA, which produced an event as well: eWalletAUpdate. This brand new car traveled as well to all available roads, until it reached walletB and the mouth. walletB, though, when sees that car says “Gosh, I have to update my value too!” and send out another car, eWalletBUpdate. Have you grasped the concept? I hope so!

The last three lines of the main start the whole thing. They trigger three events, two eup cars and one edown car, and send this 3 cars inside our brain. Just to prove what I’ve just said, this is the output of the program:

Wallet's content increase by 1 dollar!
Wallet's A content is now: 1
Wallet's B content is now: 11
Wallet's content increase by 1 dollar!
Wallet's A content is now: 2
Wallet's B content is now: 12
Wallet's content decrease by 1 dollar!
Wallet's A content is now: 1
Wallet's B content is now: 11

It’s worth noting that the update of A and B occurs automatically, without any kind of assignment. Now you can see why FRP is so appealing: it allows you to specify relations between entities and to make this relations hold for you automatically.

Final thoughts

I hope you will forgive some mistakes and some simplification in this articles, as well as my English (I’m not a mother tongue, as you would have notice).  I’m still learning Haskell, FRP and RB, so my knowledge is far limited.

Comments are obviously welcome,

Bye!

Alfredo

About these ads

5 pensieri su “Functional Reactive Programming kick-starter guide

  1. Pingback: Hello Yampa « Daniel's Notebook

  2. I like the examples and I find the comparisons very clear and efficients. One regret though: a rule such as “father = son + 10″ hardly needs an event, as a simple (+10) arrow would do the trick. The events seem much more useful when it comes to incrementing the son’s wallet.

    Unless there is a good reason to enforce rules with events rather than arrows?

  3. I like your approach and your explanation for events.
    The example, however is unconvincing: The Python pseudo code suggests two independent instances of Wallet() while your Haskell/RB implementation creates two wallets with an obvious (if loose) coupling. In OO you would create a new class to represent the magic Father’s Wallet, which has a reference to the Son’s Wallet (or at least its .money attribute) in order to maintain the magic relationship. This is no more or less complex than your example.
    That does not invalidate Reactive Programming, it just means that your example does not bring out the key difference. The neuron metaphor is pretty good, and it shows that – when you focus entirely on events – you don’t have to know the internal structure of any participant, and you can add “dependencies” by adding components that act on all or some of the existing events.

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...