Erlang (and Elixir) strongly advocates concurrency, scalability, and fault tolerance via multiprocessing and the use of specific design patterns based on it.

An Erlang process is not an operative system process: it is faster, lighter, with much smaller memory footprint.

By spawning hundreds, thousands or even millions of these processes you can achieve great scalability, as

this recent post

explains.

The server/client architecture builds on such lightweight processes, can handle state, and is one of the keys to the

great results that can be achieved using Elixir.

Let’s build a server starting from this simple calculator module code:

defmodule Calculator do def calc(value, operator, n) do case operator do :+ -> value + n :- -> value - n :* -> value * n :/ -> value / n end end end Calculator.calc 50, :-, 8 # 42

This code doesn’t handle state, just as you would expect when using a functional language: for multiple operations you

need to pipe the functions:

Calculator.calc(50, :-, 8) |> Calculator.calc(:-, 21) |> Calculator.calc(:*,2)

Spawning an Erlang process is very easy, fast and cheap:

pid = spawn fn -> end

The variable pid is a reference to the process and can be used to communicate with it.

A server is basically a never-ending process

that recursively handles some kind of requests. Let’s build some looping process first, with the ability to

handle state as well. Copy and paste this code in your iex shell:

defmodule Server do def start do spawn fn -> loop(1) end end defp loop(n) do IO.puts "looping #{n} times" :timer.sleep 1000 loop n+1 end end Server.start # looping 1 times # looping 2 times # ...

Every second a new message is printed on the screen: these letters come from the spawned server process,

which is non blocking (you can still use the repl) and kind of stateful

(the number is incremented after each iteration).

Let’s go back to our calculator example. Let’s write the CalcServer module that complements the

Calculator core module. Here the loop function will at first handle the incoming

messages, then recursively call itself with the new updated state:

defmodule CalcServer do import Calculator def start do spawn fn -> loop(0) end end def loop value do new_value = receive do {:value, caller} -> send caller, value value {operator, n} -> calc(value, operator, n) end loop new_value end end

Let’s play a little with it:

calc = CalcServer.start send calc, {:+, 23} send calc, {:-, 2} send calc, {:*, 2}

The variable calc references the calculator server process (returned by spawn ),

so we can send messages to it. These messages then get pattern-matched in the loop

receive block, precisely here:

{operator, n} -> calc(value, operator, n)

The subsequent loop function will be called with the calculation result,

courtesy of the Calculator module and its function calc that has

been imported using the import Calculator directive.

The messages we sent up until this point were fire-and-forget, meaning we didn’t receive any response from the server.

But now we need to read the total, so we can resort to bidirectional communication. First we need to send

the appropriate message to the server, which replies to our process with another message that we can read in our

main process:

send calc, {:value, self} receive do total -> IO.puts "Current total is #{total}" end # Current total is 42

We have successfully built from scratch a simple server that relies on processes and asyncronous communication.

Of course this is buggy and trivial code, but it was useful to analyze some details behind the GenServer architecture.

Now that we have a grasp of how things works, we can convert the existing code to use the GenServer behaviour. GenServer stands for “generic server”,

an abstraction/design-pattern that helps build servers with consistent and predictable interface. Here is the new server code:

defmodule CalcGenServer do use GenServer import Calculator def start do GenServer.start __MODULE__, 0 end def handle_call :value, _, value do {:reply, value, value} end def handle_cast {operator, n}, value do {:noreply, calc(value, operator, n)} end end

In order to use the GenServer behaviour we only need to define a few functions: start , with the

module name to delegate calls and casts (here the current module with __MODULE__ ), and the start value 0 .

Casts are requests that don’t need an answer, fire-and-forget. Calls are requests that require an answer, and here we have

one example of each: reading the total value is a call, while operations with the calculator are handled with a cast.

Now, here is how we can use the new server:

{:ok, calc} = CalcGenServer.start GenServer.cast calc, {:+, 84} GenServer.cast calc, {:/, 2} GenServer.call calc, :value # 42.0

This is fairly straightforward, and we don’t need to listen explicitly for responses anymore, as we did with the

previous example.

In this post we just scratched the surface of GenServer, for a detailed introduction and explanation I strongly recommend reading the official Elixir

documentation.

Happy coding!