, , , ,

Why on earth would anyone want to take the #1 selling point of the Erlang language, it’s native support for distributed processes, and use them in a local environment?

Well for the last couple of hours, I did exactly this as part of running through the “tut17” part of the Erlang Getting Started tutorial at http://www.erlang.org/doc/getting_started/conc_prog.html#id66822.

And because it wasn’t the most straightforward of tasks, I thought it might be worthwhile sharing what I learnt, even if a lot of that learning is “that doesn’t work for some reason, this does”.

So my environment is:

  • Operating System: OSX Lion (10.7.2)
  • Erlang: R15B

And the source code I wanted to run was the Ping Pong example from the tutorial, which basically sets up a simple protocol between two Erlang processes that demonstrates how processes are identified through the PID and messages are subsequently sent.

This is the full text of the code being tested:


-export([start_ping/1, start_pong/0,  ping/2, pong/0]).

ping(0, Pong_Node) ->
    {pong, Pong_Node} ! finished,
    io:format("ping finished~n", []);

ping(N, Pong_Node) ->
    {pong, Pong_Node} ! {ping, self()},
        pong ->
            io:format("Ping received pong~n", [])
    ping(N - 1, Pong_Node).

pong() ->
        finished ->
            io:format("Pong finished~n", []);
        {ping, Ping_PID} ->
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,

start_pong() ->
    register(pong, spawn(tut17, pong, [])).

start_ping(Pong_Node) ->
    spawn(tut17, ping, [3, Pong_Node]).

The tutorial talks about how to run this exercise on a single machine obliquely, but the instructions are given assuming you have 2 machines available, which is an extra layer of complexity that I didn’t want to deal with at this point.  In retrospect, I think not running on two different machines added complexity.

So the first step to locally distributed happiness is to start each two Erlang REPL instances, one to house each process.  These instances are more correctly referred to as “nodes” I believe, so I’ll use that terminology from now on.  Each node should be uniquely identified so the processes running on the node can be located.  This can be achieved a couple of different ways according to the documentation: in practice I found that using a combination of the “-name” (long name) and IP address was the only combination that worked 

This is how it looks for node 1:

{13:54}[1.9.3]~/Code/erlang ➭ erl -name ping@
Erlang R15B (erts-5.9)  [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]

And for node 2:

{14:01}~/Code/erlang ➭ erl -name pong@
Erlang R15B (erts-5.9)  [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]

That done, you need to use the net_adm module to check for awareness (and therefore connectivity) between the two nodes.  To do this, use functions from the net_adm module:

(pong@> net_adm:names().


(ping@> net_adm:names().

You should be able to see that each node can see the other.

From this point, you should be able to ping each node (and get a successful “pong” response) from the other as well.  Note the use of the single quotes when addressing nodes using IP addresses.

(ping@> net_adm:ping('pong@').


(pong@> net_adm:ping('ping@').

If everything is OK up until this point, the tutorial code should work just fine.

(pong@> tut17:start_pong().
Pong received ping
Pong received ping
Pong received ping
Pong finished


(ping@> tut17:start_ping('pong@').

Ping received pong
Ping received pong
Ping received pong
ping finished

Now that we can see the happy path, let me show you what doesn’t work.

  • Starting nodes using the shortname (“-sname”) option
  • Starting nodes using invalid IP addresses (e.g., “”)
  • Naming nodes using hostname (e.g., “localhost”)
  • Specifying nodes without using quotes (“net_adm:ping(pong@”)

And finally this is what didn’t really help or hinder the activity.

  • Explicitly setting the Erlang Cookie using either file or programmatically
  • Using the .hosts.erlang file