07 February 2016

Recently had to explain the difference between GenServer message types to handle TCP protocols.

Here’s a simple HelloServer handling call, cast and info messages.

defmodule HelloServer do
  use GenServer

  def handle_call(msg, _from, state) do
    {:reply, "Received in call: #{msg}", state}
  end

  def handle_cast(msg, state) do
    :timer.sleep 2000
    IO.puts "Received in cast: #{msg}"
    {:noreply, state}
  end

  def handle_info(msg, state) do
    IO.puts "Received in info: #{msg}"
    {:noreply, state}
  end
end

Load this in iex and try sending messages to the process.

$ iex hello-server.ex

iex> {:ok, pid} = GenServer.start HelloServer, []
{:ok, #PID<0.70.0>}

call

A call is synchronous. It’ll return the value upon execution.

iex> GenServer.call pid, "hello"
"Received in call: hello"

The result can be assigned to a variable since the call returns the value.

iex> result = GenServer.call pid, "hello"
iex> IO.puts result
"Received in call: hello"

It blocks the calling process.

cast

A cast is asynchronous. It does not block the calling process.

The VM will say “:ok, I’ve received the message” and then process it later.

iex> GenServer.cast pid, "hello"
:ok
Received in cast: hello

I purposefully put a :timer.sleep call to make the function sleep for 2 seconds and execute slowly. So now you see that Erlang is executing the function in the background after it returns :ok.

The return value is :ok and the execution happens in the background. Storing the result in a variable is less useful.

info

An info message is anything that is not a call or a cast message. All messages that are sent to a process directly (instead of via call or cast) will end up here. It is asynchronous like cast and does not block the calling process.

iex> send pid, "hello"
Received in info: hello
"hello"

send is the Kernel.send function. It only returns the message sent.