Exploring Elixir processes
Expanding on our FizzBuzz example from the previous post.

Almost 3 months have passed since the release of Elixir 1.0, but we are still very excited about the language.
In this blog post we’ll expand on the FizzBuzz example from the previous post, which we’ll now turn into a server.
The FizzBuzz server
defmodule FizzBuzz do
@moduledoc "A very simple FizzBuzz server"
@doc """
Computes and outputs the FizzBuzz value for the
provided argument.
"""
@spec compute(Integer) :: :ok
def compute do
receive do
n -> IO.puts "#{n}: #{compute(n)}"
end
compute
end
defp compute(n) do
case {rem(n, 3), rem(n, 5)} do
{0, 0} -> :FizzBuzz
{0, _} -> :Fizz
{_, 0} -> :Buzz
_ -> n
end
end
end
lang-elixir
This is a simple module, with some documentation and a typespec for compute/0, which works as the main entry point for the FizzBuzz module. It uses receive/1 to get the next number from the process’ mailbox, computes (via the private compute/1 function) and outputs the result, before calling itself again to wait for the next message.
Communicating with the server
To use our newly created server, we first start it with spawn/3, which expects a module name, the name of the function to be called as well as that function’s arguments. It returns the pid of the newly created process, which is stored in the variable fb (remember that parenthesis around function arguments are optional in Elixir).
fb = spawn FizzBuzz, :compute, []
lang-elixir
We then can use send/2 to send a message to the server:
send fb, 3
lang-elixir
This will print “3: Fizz” and return 3. Now let’s send several messages to make sure the recursive call in compute/0 works as expected:
10..15 |> Enum.map(&send(fb, &1))
lang-elixir
Here we use Elixir’s pipe operator (|>/2) to send the Range of numbers from 10 to 15 into Enum.map/2, which uses a partially applied version of send/2 with an anonymous argument (&1). The output is as expected:
10: Buzz
11: 11
12: Fizz
13: 13
14: 14
15: FizzBuzz
lang-elixir
Dealing with errors
At this point the more observant readers may wonder what happens if instead of an integer, we send some other data type to FizzBuzz.compute/0. Let’s try:
send fb, 1.0
lang-elixir
This leads to the following error message:
14:25:32.742 [error] Error in process <0.95.0> with exit value: {badarith,[{'Elixir.FizzBuzz',compute,1,[{file,"fizzbuzz.ex"},{line,10}]},{'Elixir.FizzBuzz',compute,0,[{file,"fizzbuzz.ex"},{line,4}]}]}
lang-elixir
This means that the FizzBuzz server crashed. However, since we were dealing with an unlinked process, our main program (in this specific case an iex session) is unaffected by this. Now let’s start the server again, but this time as a linked process using spawn_link/3:
fb = spawn_link FizzBuzz, :compute, []
lang-elixir
Now a problems in our FizzBuzz server will bubble up and also terminate the main process:
14:33:40.426 [error] Error in process <0.119.0> with exit value: {badarith,[{'Elixir.FizzBuzz',compute,1,[{file,"fizzbuzz.ex"},{line,10}]},{'Elixir.FizzBuzz',compute,0,[{file,"fizzbuzz.ex"},{line,4}]}]}
** (EXIT from #PID<0.116.0>) an exception was raised:
** (ArithmeticError) bad argument in arithmetic expression
fizzbuzz.ex:10: FizzBuzz.compute/1
fizzbuzz.ex:4: FizzBuzz.compute/0
lang-elixir
This could be rectified by adding a guard clause to compute/0 in our FizzBuzz model like this:
def compute do
receive do
n when is_integer(n) -> IO.puts "#{n}: #{compute(n)}"
_ -> IO.puts "Invalid argument"
end
compute
end
lang-elixir
While this works, it goes against one of Erlang’s most important principles, “let it crash”. Instead of programming defensively, errors will just terminate a process and leave it up to monitoring processes (if there are any) to deal with this situation.
Monitoring
One way to deal with the above is to set up a monitor. Unlike a link this is a unidirectional relationship between processes. To create a monitor, we start the FizzBuzz server with spawn_monitor/3, which on top of the pid will also return a monitoring reference. We are not using this reference in the following example, but it’s useful for manually disabling monitoring with a call to demonitor/2.
Now in case the server dies, a message will be sent to the monitoring process:
{fb, ref} = spawn_monitor FizzBuzz, :compute, []
send fb, 1.0
lang-elixir
Since we didn’t use receive/1 to set up a message receiver in our iex session, we will have to manually call the flush/0 helper to see the monitoring alert:
{:DOWN, #Reference<0.0.0.262>, :process, #PID<0.79.0>,
{:badarith,
[{FizzBuzz, :compute, 1, [file: 'fizzbuzz.ex', line: 16]},
{FizzBuzz, :compute, 0, [file: 'fizzbuzz.ex', line: 10]}]}}
lang-elixir
What’s next?
Dealing with processes in Elixir is very easy. However, by implementing the FizzBuzz server from scratch, we are not making full use of the power that the Erlang/OTP underpinnings provide to Elixir. So in the next installment of this series we’ll revisit the FizzBuzz server, but this time making use of the gen_server and supervisor OTP behaviors. Stay tuned!


