Process

Process follows the Actor model.

Spawn a process

Use spawn to create a new process, it will return a Process Identifier (PID):

defmodule Adder do
  def add(a, b) do
    IO.puts(a + b)
  end
end
iex> pid = spawn(Adder, :add, [1, 1])
2
#PID<0.110.0>

Receiving and sending messages

Use send to send message to a process:

defmodule Foo do
  def listen do
    receive do
      msg -> IO.puts("received #{msg}")
    end
  end
end
iex> pid = spawn(Foo, :listen, [])
#PID<0.117.0>
iex> send(pid, "hello")
received hello

But if you send message to the process again, nothing happens, the process is dead.

iex> Process.alive?(pid)
false

To fix this, we need to call listen recursively:

defmodule Foo do
  def listen do
    receive do
      msg -> IO.puts("received #{msg}")
    end
    listen()
  end
end

Linked Process

If you need to make sure the process is spawned succssfully. Use spawn_link instead of spawn will link the newly created process to the current one. Two linked processes will receive exit signals from one anther.

defmodule Foo do
  def boom do
    exit(:boom)
  end
end

pid = spawn_link(Foo, :boom , [])
** (EXIT from #PID<0.104.0>) shell process exited with reason: :boom

By default the exit signal will crash the process.

Trap the exit signal

Using Process.flag(:trap_exit, true) to trap exit notifications.

defmodule Foo do

  def boom do
    exit(:boom)
  end

  def run do
    Process.flag(:trap_exit, true)

    spawn_link(Foo, :boom, [])

    receive do
      {:EXIT, _pid, reason} -> IO.puts("Exit reason: #{reason}")
    end
  end
end
iex> Foo.run
Exit reason: boom

Monitoring

Another way is to use spawn_monitor to receive process down signal:

defmodule Foo do
  def boom do
    exit(:boom)
  end

  def run do
    spawn_monitor(Foo, :boom , [])

    receive do
      {:DOWN, _ref, :process, _from_pid, reason} -> IO.puts("Exit reason: #{reason}")
    end
  end
end

Foo.run