Out of all the languages that I never get to program in, Erlang is probably my favorite. I used it while I was working on my Ph.D., and a few times in private projects since then, but there's very little consulting work going for Erlang programmers, so it's rare for me to get to use it.
That said, when I started learning Go, Erlang was the first thing that came to mind. It's also the language mentioned in most common criticisms of Go that I encounter when I talk about my new Go Phrasebook. A common complaint is that Erlang's concurrency model is much cleaner than Go's. In this article, I'll look at both and show that it's possible to express either in terms of the other-neither language is inherently superior, but both expose different models to the programmer.
One thing that may confuse people with a theoretical computer science background is that Erlang takes some syntax from Hoare's Communicating Sequential Processes (CSP) model, but with different semantics, whereas Go takes the semantics but not the syntax. More confusingly, Erlang's semantics are fairly close to early versions of CSP, but not to later ones.
Actors and Processes
Simula was the first language to provide support for the actor model used by Erlang. This model was also proposed for Smalltalk, but only in Actor Smalltalk did the language get a full implementation.
In the actor model, you have a set of actors, each of which runs independently, which can communicate by sending asynchronous messages to each other. This is more or less the definition of object orientation (although object orientation doesn't specify that the messages have to be asynchronous). Erlang's actor model gives it the distinction of being the only language that claims not to support object orientation, while actually providing full support for it. Languages that claim to support object orientation but don't are far more common.
The important thing about the actor model is that messages are addressed to specific actors. Erlang calls these processes. When you create a process, you get a process identifier, which you can use as the recipient for messages.
Communicating Sequential Processes
In the CSP model, things are subtly different. The basic unit of execution is still a process, which runs sequentially but communicates with others by message passing. The key difference between the two models is that CSP introduces the notion of a channel. Processes don't communicate with each other directly; instead, they send events (messages) to channels, and receive events from channels.
A channel is like a pipe. In the CSP model, message delivery is instantaneous and synchronous. The Go implementation is slightly different: Messages are buffered in channels in much the same way that Erlang buffers messages for delivery to processes.
This difference means that a layer of indirection is present in CSP that isn't present in the actor model. Go unfortunately misses out on one of the things that makes CSP easy to reason about. The synchronous nature of CSP message-passing means that messages are never lost. A process won't proceed past the point where it sends a message, until another process is capable of receiving that message. In Go, the buffering-especially combined with garbage collection-means that you have to be quite careful. It's possible for a channel to exist with no references to the receiving end. You can still put messages into it until the buffer is full, or possibly until the garbage collector deletes the last receiving end and flags the channel as deadlock-inducing.