Side effects in implementing multiple processes

There is this code in programming elixir 1.3
Seems like the use of a global registry is a side effect. Is there any better way to do this or is this the “recommended” way?

defmodule Ticker do

  @interval 2000   # 2 seconds
  @name     :ticker

  def start do
    pid = spawn(__MODULE__, :generator, [[]])
    :global.register_name(@name, pid)
  end

  def register(client_pid) do
    send :global.whereis_name(@name), { :register, client_pid }
  end

  def generator(clients) do
    receive do
      { :register, pid } ->
        IO.puts "registering #{inspect pid}"
        generator([pid|clients])
    after
      @interval ->
        IO.puts "tick"
        Enum.each clients, fn client ->
          send client, { :tick }
        end
        generator(clients)
    end
  end
end

defmodule Client do

  def start do
    pid = spawn(__MODULE__, :receiver, [])
    Ticker.register(pid)
  end

  def receiver do
    receive do
      { :tick } ->
        IO.puts "tock in client"
        receiver
    end
  end
end

Well, the code in the book is written a particular way in order to teach you particular things. In production code you basically never use spawn directly, nor do you manage your own receive loop.

Seems like the use of a global registry is a side effect.

Yes, it is.

Is there any better way to do this or is this the “recommended” way?

It depends on what “this” is. If “this” were to just be "print tick every N seconds` then there’s definitely more “proper” ways to do it. But in the context of the book, “this” is “learn these things”.

1 Like

“this” meaning passing message to another node and have that node respond back without using the global registry / side effects

It’s a concession to practicality:

  • If you have a PID you can simply use that - the issue is how do you get that PID in the first place? At the very minimum the registered name gives you a first point of contact; that first point of contact could give you a PID to use for the rest of the conversation - and that PID could be different from the one returned by Process.whereis(name).
  • However there is also the issue of fault tolerance. Once a process terminates the PID is useless as the process that may replace it will have a different PID - a registered name gets around that problem as the replacement process can be registered under the name.