Elixir Project: Counter Server
A counter server is a process that holds one number and changes it only when it receives a message. This project builds that server, sends it a scripted run of messages, and prints the replies it sends back.
The loop answers one read with 2
The loop handles :inc and a read request. Two increments arrive before the read, so the reply is 2.
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)An add message moves the count by 5
The full loop also handles {:add, n}. An add of 5 and one increment give a reply of 6.
defmodule Counter do
def loop(count) do
receive do
:inc ->
loop(count + 1)
{:add, n} ->
loop(count + n)
{:read, sender} ->
send(sender, {:count, count})
loop(count)
end
end
end
counter = spawn(Counter, :loop, [0])
send(counter, {:add, 5})
send(counter, :inc)
send(counter, {:read, self()})
count =
receive do
{:count, n} -> n
end
IO.puts(count)Two reads build a receipt list
The script reads the count twice during one run. The two replies form a labelled receipt list.
defmodule Counter do
def loop(count) do
receive do
:inc ->
loop(count + 1)
{:add, n} ->
loop(count + n)
{:read, sender} ->
send(sender, {:count, count})
loop(count)
end
end
end
counter = spawn(Counter, :loop, [0])
send(counter, :inc)
send(counter, {:read, self()})
first =
receive do
{:count, n} -> n
end
send(counter, {:add, 4})
send(counter, {:read, self()})
second =
receive do
{:count, n} -> n
end
IO.inspect([first: first, second: second])Example
defmodule Counter do
def loop(count) do
receive do
:inc ->
loop(count + 1)
{:add, n} ->
loop(count + n)
{:read, sender} ->
send(sender, {:count, count})
loop(count)
end
end
end
counter = spawn(Counter, :loop, [0])
send(counter, :inc)
send(counter, :inc)
send(counter, {:add, 5})
send(counter, {:read, self()})
first =
receive do
{:count, n} -> n
end
send(counter, {:add, 3})
send(counter, {:read, self()})
second =
receive do
{:count, n} -> n
end
receipts = [first: first, final: second]
IO.inspect(receipts)
IO.puts(second)Key points
- State loop: a recursive loop function holds the count as its argument and calls itself with each new count.
- Spawn: spawn(Counter, :loop, [0]) starts the counter with a count of 0.
- Increment: the message :inc tells the loop to continue with the count plus 1.
- Add: the message {:add, n} tells the loop to continue with the count plus n.
- Read: the message {:read, sender} tells the loop to send {:count, count} back to the sender.
- Receipts: replies collected in send order form a list that shows the count after each read.
In this example, the Counter loop holds the count and handles :inc, {:add, n}, and {:read, sender}, and each read replies with {:count, count}. The script starts the counter at 0, sends two increments and an add of 5, and reads the count back as 7. It adds 3 more and reads 10, then prints the labelled receipt list and the final count, 10.
Note
the counter keeps its state only in the argument of its recursive call, with no shared variable anywhere.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
The count is 6 and the server receives {:add, 4}. What count does the loop continue with?