🎓 A1Learner
🔍
0 of 54 lessons completed

Elixir Process State

Process state is a value a long-running process carries while it works. A counter process carries its current count from one message to the next.

A fresh counter reads back zero

The counter starts its loop with 0. A read message asks for the count, and the reply carries it back.

defmodule Counter do
  def loop(count) do
    receive do
      :inc -> loop(count + 1)

      {:read, sender} ->
        send(sender, {:count, count})
        loop(count)
    end
  end
end

counter = spawn(Counter, :loop, [0])
send(counter, {:read, self()})

count =
  receive do
    {:count, n} -> n
  end

IO.puts(count)

One :inc raises the count to one

The :inc message is sent before the read message. Send order means the increment is handled first.

defmodule Counter do
  def loop(count) do
    receive do
      :inc -> loop(count + 1)

      {:read, sender} ->
        send(sender, {:count, count})
        loop(count)
    end
  end
end

counter = spawn(Counter, :loop, [0])
send(counter, :inc)
send(counter, {:read, self()})

count =
  receive do
    {:count, n} -> n
  end

IO.puts(count)

State survives between reads

The first read answers 0, then one :inc lands, and the second read answers 1. The count lived inside the counter the whole time.

defmodule Counter do
  def loop(count) do
    receive do
      :inc -> loop(count + 1)

      {:read, sender} ->
        send(sender, {:count, count})
        loop(count)
    end
  end
end

counter = spawn(Counter, :loop, [0])
send(counter, {:read, self()})

first =
  receive do
    {:count, n} -> n
  end

send(counter, :inc)
send(counter, {:read, self()})

second =
  receive do
    {:count, n} -> n
  end

IO.puts(first)
IO.puts(second)

Example

defmodule Counter do
  def loop(count) do
    receive do
      :inc -> loop(count + 1)

      {:read, sender} ->
        send(sender, {:count, count})
        loop(count)
    end
  end
end

counter = spawn(Counter, :loop, [0])
send(counter, :inc)
send(counter, :inc)
send(counter, {:read, self()})

count =
  receive do
    {:count, n} -> n
  end

IO.puts(count)

Key points

  • Carrier: the loop function takes the current state as an argument.
  • Update: to change state, the loop calls itself with the new value as the argument.
  • Read: a read message carries the sender PID so the loop knows where to reply.
  • Reply: the loop sends the state back and calls itself with the same state.
  • Order: one sender's messages are handled in send order, so a read sent after two :inc messages sees both.
  • Private: the state lives inside the process, and outside code reaches it only through messages.

In this example, the counter is spawned with a starting count of 0 and then receives two :inc messages. Each :inc makes the loop call itself with the count plus one. The read is handled last, so the reply is {:count, 2} and the script prints 2.

Note

nothing is changed in place here, because each loop call simply receives the next count as a fresh argument.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

How does the counter move from a count of 1 to a count of 2?