noobtuts

Handling Clojure state the Erlang way

Foreword

The oldest of all questions: how do we keep track of a value without defining it globally and then changing it all the time?

We all know that Clojure has its own phenomenal way to take care of state, with refs, atoms and vars.

The Situation

For example, let's say we want to make a game server that has to keep track of the amount of received packets. The obvious way would be to use an atom:

(def n (atom 0))

(defn the-thread []
  (when (recv-packet) ; we received a packet
    (swap! n inc))    ; increase counter
  (recur))            ; infinite loop...

Which will work just fine forever. Now all these things like atoms and refs and vars are unbelievably helpful when we come to situations where the score should be accessed by several threads simultaneously.

Note: they are helpful because they allow us to use things like transactions.

The 'simple' state

However in some cases (like our Clojure 2D Pong Game) there are some values that are state, but are only accessed by one thread all the time (which is quite common in most projects).

Of course our atom does that just fine too, but its still that thing that we define globally, which always should be avoided to keep state (and headaches) as far away as possible.

The Erlang Way

Let's take a look over to Erlang and see how state is taken care of there. Without going into detail what an Erlang process is, here is how our above situation would be handled by an Erlang programmer:

(defn the-thread [n]   ; here is where n is stored
  (if (recv-packet)    ; received a packet?
      (recur (inc n))  ; infinite loop... next time with n+1
      (recur n)))      ; infinite loop... with the same n

Or in other words: Erlang's processes always use recursion + function parameters to store values.

There are several benefits to this approach. For one its the more elegant code, but its also the fact that we don't have to define a global variable anymore. There is no way that anyone ever has to worry about that globally defined variable, or that anyone ever accidentally changes it. The n will live happily ever after within our function, and no one will ever see it.

This concept can be quite mind blowing when seen the first time around.

A slightly more complex Example

The above example still looks a bit obvious, but let's look at a slightly more complex example. Its a process that stores a counter, can increment it if asked to, can decrement it if asked to, can respond with the value of the counter or stop if asked to.

In Erlang

Let's look at the Erlang code at first:

counter(N) ->
    receive
        {increment} ->
            counter(N + 1);
        {decrement} ->
            counter(N - 1);
        {show, From} ->
            From ! N,
            counter(N);
        {stop} ->
            exit(normal)
    end.

The receive and end clause mean 'wait for a message to be sent by someone'. The stuff surrounded by curly brackets like {increment} means 'if message == "increment" then do...'. As soon as someone tells the process to increment the counter, it will just continue recursively with an increased n.

Just plain beauty!

In Clojure

Here is the same thing in Clojure:

(defn counter [n]
  (case (recv-packet)
    :increment (recur (inc n))
    :decrement (recur (dec n))
    :show (do (send-packet n) (recur n))
    :stop nil))

Powerful and beautiful at the same time - and all that without state but with a self contained process that stores things and does things if asked to.

Note: of course one could argue that there is some kind of state within the function, which however is still far less annoying than global variables.