DEV Community

Cover image for Process communication in Elixir
ThiagosLima
ThiagosLima

Posted on

Process communication in Elixir

The focus of this post is to explain the communication between processes in elixir in a simple way. Here you can read the portuguese version of this article. When you've finished reading, you'll have a basic understanding of how to create processes and how they send and receive messages.

How to create processes

A process is something that can do things in isolation and concurrently. Well... this is pretty vague. Let's try a more concrete example. Let's say you are a mouse process. You can do many things that mice do, like hiding or eating cheese for example. You are independent, you don't need other people to do what you want and they don't interfere with your activities. That is, you do it in isolation. In a mice contest to see who eats the most cheese, everyone could do it concurrently and in parallel. Everyone eats at the same time and not one at a time.

A mouse process

Now that we have an idea of what a process is, let's imagine that you want to make a new friend, that is, another process. So let's create the cat process. We can do this with the spawn function.

Spawn a new process

The spawn function creates a new process and performs a function on it. It has two arities: spawn/1 takes a function and spawn/3 takes a module, a function as an atom and a list of parameters. Here we are going to use the second form because we are going to define Cat in a module. The create function just prints a message. You can put the code below in a file called mouse.exs and run it with elixir mouse.exs. If you do this you will see the commented result.

# mouse.exs
defmodule Cat do
  def create do
    IO.puts("You create cat")
  end
end

# Here you create a cat
spawn(Cat, :create, [])

# Result:
# "You create cat"
Enter fullscreen mode Exit fullscreen mode

Communication between processes

After creating a new process, your friend the cat, you want to talk to him. But remember that processes are isolated. Imagine that everyone lives in their own home with social distance. So how to communicate with him? Millions of years ago people used a form of communication called mail. Processes communicate in a similar way. So to talk to your friend you will send a message like that.

Image description

Sending and receiving messages

We send a message with the send/2 function. We pass to this function two things: the receiver and the message. But how will our code know how to send the message to the cat process? Thankfully, the spawn function returns an identifier of the process created for us, as if it were its address. So let's save the spawn return in the variable cat.

cat = spawn(Cat, :create, [])
Enter fullscreen mode Exit fullscreen mode

The cat needs to know how to receive this message. For this we defined the receive block. Here's how it works: the cat will look inside the mailbox and decide what is important. That is, for which messages he wants to do something.

# mouse.exs
defmodule Cat do
  def create do
    receive do
      :how_is_it_going -> IO.puts("Good!")
    end
  end
end

# Here you create a cat
cat = spawn(Cat, :create, [])

# Send the message
send(cat, :how_is_it_going)

# Result:
# "Good!"
Enter fullscreen mode Exit fullscreen mode

Answering back

Our code has a problem. Imagine IO.puts("Good!") as if the cat had said that. But you are in your own homes, remember? So you didn't hear his answer. Because processes communicate with messages, he would have to send you a message back.

Send message back

Glad you already know how to send a message. Just make the cat respond with send(mouse, :good) and it's all right! However, how will the cat know that the message came from the mouse? To do this, let's say in the message who the sender was. We have to change the cat's receive to care for messages that follow the pattern {:how_is_it_going, sender}. So he can reply to the right person with send(sender, :good). It looks like this:

receive do
  {:how_is_it_going, sender} -> send(sender, :good)
end
Enter fullscreen mode Exit fullscreen mode

Now the mouse needs to identify itself to send the message send(cat, {:how_is_it_going, mouse}). Remember that the mouse is the current process. To identify the current process in the code we use the self/0 function. It's like we're getting our own address. So we save its return in a variable mouse = self().

# This is you
mouse = self()

# Send the message
send(cat, {:how_is_it_going, mouse})
Enter fullscreen mode Exit fullscreen mode

Almost everything is ready. We just need to look in the mouse's mailbox for the message the cat sent and decide what to do. The entire code would look like this:

# mouse.exs
defmodule Cat do
  def create do
    receive do
      {:how_is_it_going, sender} -> send(sender, :good)
    end
  end
end

# This is you
mouse = self()

# Here you create a cat
cat = spawn(Cat, :create, [])

# Send the message
send(cat, {:how_is_it_going, mouse})

# Look in the mouse's mailbox
receive do
  :good -> IO.puts("Nice!")
end

# Result:
# "Nice!"
Enter fullscreen mode Exit fullscreen mode

Temporary Process

You can check whether a process is alive with the Process.alive?/1 function. Let's run the same code above but see when the cat process is alive by adding IO.puts("Is the cat alive? #{Process.alive?(cat)}").

# mouse.exs
defmodule Cat do
  def create do
    receive do
      {:how_is_it_going, sender} -> send(sender, :good)
    end
  end
end

# This is you
mouse = self()

# Here you create a cat
cat = spawn(Cat, :create, [])

IO.puts("Is the cat alive? #{Process.alive?(cat)}")

# Send the message
send(cat, {:how_is_it_going, mouse})

# Look in the mouse's mailbox
receive do
  :good -> IO.puts("Nice!")
end

IO.puts("Is the cat alive? #{Process.alive?(cat)}")

# Result:
# Is the cat alive? true
# Nice!
# Is the cat alive? false
Enter fullscreen mode Exit fullscreen mode

We can see that the process was alive, but after answering it died. Why did this happen?

Dead process

To understand what happened let's look at the create function. Here we defined a receive block. Since the mouse received the message back, communication is correct. But what happens after the cat responds? Nothing else happens in the cat process. As such, it no longer needs to exist and the process dies!

def create do
  receive do
    {:how_is_it_going, sender} -> send(sender, :good)
  end
end
Enter fullscreen mode Exit fullscreen mode

Permanent Process

To keep the process alive, let's make sure it doesn't respond to just one message. It will keep checking messages and replying. We can do this by defining the check_messages/0 function and passing the receive block to it.

def create do
  check_messages()
end

def check_messages do
  receive do
    {:how_is_it_going, sender} -> send(sender, :good)
  end

  check_messages()
end
Enter fullscreen mode Exit fullscreen mode

So the only thing the create function does is to tell the cat to check the messages. Now look at the last line of check_messages/0. It calls itself! That is, whenever it ends, it goes back to the beginning. It's a recursive function. That way the cat checks messages forever and doesn't die. The whole code looks like this:

# mouse.exs
defmodule Cat do
  def create do
    check_messages()
  end

  def check_messages do
    receive do
      {:how_is_it_going, sender} -> send(sender, :good)
    end

    check_messages()
  end
end

# This is you
mouse = self()

# Here you create a cat
cat = spawn(Cat, :create, [])

IO.puts("Is the cat alive? #{Process.alive?(cat)}")

# Send the message
send(cat, {:how_is_it_going, mouse})

# Look in the mouse's mailbox
receive do
  :good -> IO.puts("Nice!")
end

IO.puts("Is the cat alive? #{Process.alive?(cat)}")

# Result:
# Is the cat alive? true
# Nice!
# Is the cat alive? true
Enter fullscreen mode Exit fullscreen mode

Conclusion

Here you learned the basics of how to create processes with spawn. How they send messages with send/2 and receive messages with the receive block. You saw a little bit about how a process can die and how to keep it alive. And the best of all, cats and mice can be friends after all!

Resources

Discussion (0)