This is Part 3 of Elixir for the Lazy, Impatient and Busy - Processes 102! If you haven’t looked at Part 1 on Lists and Recursion and Part 2 Processes 101, I encourage you to check those out first.
Quick Recap
Previously, we looked at creating Elixir processes using spawn
, then we played with a little bit of message passing using the <-
operator.
We took a tiny detour to examine the Actor concurrency model, and also noted that processes are relatively cheap in Elixir.
We left off with this, and noted that there was a big limitation to this code:
defmodule Helloer do
def hola do
receive do
{sender, msg} ->
sender <- { :ok, "Received: '#{msg}'. Thank you!" }
end
end
end
helloer_pid = spawn(Helloer, :hola, [])]
helloer_pid <- {self, "Here's a message for you!"}
receive do
{ :ok, msg } -> IO.puts msg
end
What’s wrong?
When you run the above program, you will receive a cheerful message:
"Received: 'Here's a message for you!'. Thank you!"
Let’s now send another message.
We’ll do it the simplest way possible, by appending another receive
construct like so:
defmodule Helloer do
def hola do
receive do
{sender, msg} ->
sender <- { :ok, "Received: '#{msg}'. Thank you!" }
end
end
end
helloer_pid = spawn(Helloer, :hola, [])]
helloer_pid <- {self, "Here's a message for you!"}
receive do
{ :ok, msg } -> IO.puts msg
end
# Let's send another message
helloer_pid <- {self, "Here's ANOTHER message for you!"}
receive do
{ :ok, msg } -> IO.puts msg
end
And to our utter horror:
We get "Received: 'Here's a message for you!'. Thank you!"
and then … nothing!
It just hangs there indefinitely! What’s going on?
hola
is the culprit
Look at hola
again. What happens when it receives a message?
The message comes in, then it sends back a message of its own to sender
. Then it simply exits. There’s nothing to tell hola
that it should carry on waiting for messages.
So how do we allow hola
to receive as many messages as we like? Turns out, the answer is pretty simple.
Recursion has got our back!
The simplest way would be to call itself again. Here’s the modified hola
:
def hola do
receive do
{sender, msg} ->
sender <- { :ok, "Received: '#{msg}'. Thank you!" }
hola # <--- Recursive call ---
end
end
Run it again, and lo and behold:
"Received: 'Here's a message for you!'. Thank you!"
"Received: 'Here's ANOTHER message for you!'. Thank you!"
Minor Gotcha: Where to place the recursive call?
I have made this mistake enough times than I have cared to admit:
def hola do
receive do
{sender, msg} ->
sender <- { :ok, "Received: '#{msg}'. Thank you!" }
end
hola # <--- Don't do this! ----
end
Always put your recursive call inside the receive
block. Otherwise, when the message is received, the process would exit, without ever reaching the recursive call.
Bonus: :pman
: The Process Manager
Launch iex
in the terminal, invoke the process manager:
iex> :pman.start
Next time …
You might be slightly sick of processes already, but I would do another post on it. That’s because processes occupy such a central role in Elixir, and there a couple of other important concepts that I feel are important enough to cover.
Thanks for reading!