Why did Joe Armstrong make this decision in chapter 22.1 of "Programming Erlang, 2nd E"?

I’m speaking in reference to a file called server1.erl. The code, as-presented in his book, compiles, and the modules do what they are supposed to do inside erl.

The code I’m interested in:

File: server1.erl
...
start(Name, Mod) ->
  register(Name, spawn(fun() -> loop(Name, Mod, Mod:init()) end)).
...

File: name_server.erl

...
add(Name, Place) -> server1:rpc(name_server, {add, Name, Place}).
...

erl-repl:

server1:start(name_server, name_server).

I remember experiencing alarm bells the first time I saw that. Why is he doing that? Then I realized that I could say:

server1:start(ns, name_server).

And be able to send messages to a shorter-name atom (“because it looks cool”).

But if I do that, then I’d also have to edit my client code, because the client code is hard-coded to use “name_server” as the atom to which it sends messages.

So… I edited client and server to not use variable Name, and it still works. Then I started to recognize a few things about the original and the new that I wasn’t seeing before. I’ll stop there.

At first I thought it was a ‘mistake’, because if I recall correctly (50% chance at best), the iterations he does with server2, server3, server4, all use that same serverN:start(Name, Mod) signature.

Then I thought, or maybe I could figure out a way to parameterize the reference to “name_server” inside the client, then I could correctly say serverN:start(ns, name_server).

My final thought was that he might have done it on purpose. Because it was (to me) so very obviously ‘not right’, that a part of me assumes I must be the one who is mistaken. :slightly_smiling_face:

Why do you think Mr. Armstrong made the decision to write the code as he did?

1 Like

It’s been several years I read his book (which by the way, is an excellent book!) and I have unfortunately given it away. I think your question is about the use of a “parameterized” name server reference. Typically, you’d approach this the same way you’d look at how the processes are registered in the Erlang runtime. In other words, the register/2 function that takes the name_server atom reference is meant to be directly visible to the Erlang runtime system.

Take a look at the documentation for register/2: erlang — erts v15.2.3, which says:

-spec register(RegName, PidOrPort) → true when RegName :: atom(), PidOrPort :: port() | pid().

Registers the name RegName with a process identifier (pid) or a port identifier in the name registry. RegName, which must be an atom, can be used instead of the pid or port identifier in send operator (RegName ! Message) and most other BIFs that take a pid or port identifies as an argument.

It also has this to say about the RegName:

The registered name is considered a Directly Visible Erlang Resource and is automatically unregistered when the process terminates.

Directly visible Erlang resources are typically atoms. Erlang books would warn you early on about registering too many atoms that can crash the Erlang VM. So you’d take care to register only the processes that you need.

Hope this helps!

I (vaguely) recall a conversation I had once with Joe where I was asking about when to use pure Erlang, and when to use OTP. He said he mostly used pure Erlang, but acknowledged he was probably a special case since he understood the pitfalls that OTP largely guards against. His book aims to educate the reader on what OTP does for you by illustrating some of the core principles (specifically of a gen_server) - he even annotated my copy for additional emphasis!

1 Like

Learning Erlang is quite different from learning how to use OTP. When I train new hires I do not expose them to ! and receive as they will never use that working for me. Most Erlang learning resources put that up front.

It’s always tempting to get the new hires to write common_test cases, as we can always use more and they can’t do much harm there, however test cases are written in a style which wouldn’t be appropriate in production code.

Instead what I generally do is get them to write an application with a supervisor and a gen_server, start it and call it’s API in the shell.