intertwingly

It’s just data

Erlang: First Impressions


In Ruby, constants start with a capital letter.  In Erlang, they do too.

In Ruby, symbols start with a colon, and are typically followed by a lower case letter.  Symbols are heavily used in Ruby programs.  In Erlang atoms serve a similar purpose, and start with a lower case letter (without a sigil).  Atoms are heavily used in Erlang programs too.  Single quotes may be used to create atoms that aren’t simple identifiers.  Note that atoms which contain @ and . characters do not need to be quoted, among other things this facilitates naming things that may exist on other machines.

In Ruby, method names start with a lower case letter, and class names start with an upper case letter.  In Erlang, function names start with a lower case, and namespace names also start with a lower case letter.

In Ruby, variable names start with a lower case letter.  Erlang doesn’t have variables.  Instead, every process has a hash that you can access via built in functions, get and put.  These functions aren’t as heavily used as you might think.

Infix operators (like +) are functions in Erlang as oposed to polymorphic methods.  + add two numbers, ++ concatenates two lists.  Strings are lists in Erlang.

Like most languages these days, Erlang supports a JSON like syntax for data structures, with a few notable differences.  For starters, Erlang doesn’t have a native syntax for hash or dictionary, and does have Python-esque distinction between tuples (which are immutable) and lists (which are optimized for “head” and “tail” operations).  There is a concept of a record, which is closer to a C style “struct” than a Ruby style hash.  In any case, heavy use of atoms lead to a different style of making data structures self documenting, for example a hypertext link modeled in Erlang might look like this:

{a, [{href, "http://example.com"}]}

This is a tuple, containing an atom and a list.  This list contains a single tuple, which contains an atom and a string.

Such data structures are immutable, and can’t contain any variables or pointers.  That makes them inherently serializable, which Erlang does transparently when it passes message between processes.

Comments start with a % instead of a #.

Statements end with a period followed by whitespace.  Statements may have many clauses, separated by semicolons.  Clauses may define a number of data constructors, separated by commas.  Like in Ruby methods, the last data constructor is the return value for a function.

Functions in Erlang are a clause.  Here’s a simple one:

square(X) -> X*X.

Erlang programs don’t tend to make heavy use of traditional control structures.  Instead they tend to rely on QBE style pattern matching and recursion.  Here’s an example:

area({circle, R}) -> PI * R *. R;
area({triangle, Base, Height}) -> 0.5 *. Base *. Height.

The above defined a single function, named area/1 (the slash one indicating one argument).  When called with a tuple of length two, and with the first element matching the atom circle, R is first bound to the value of the second element, then the remainder of the clause is evaluated.  If the arguments don’t match, the next patterns is checked.  If none match, an error is generated.

In some cases, it is helpful to use pattern matching variables which start with an underscore: these essentially mean “don’t care”.

Another example:

shampoo(HAIR) when not clean(HAIR) ->
  lather(HAIR),
  rince(HAIR),
  shampoo(HAIR);
shampoo(HAIR) -> HAIR.

This fictitious example demonstrates both gates that can’t be expressed by simple pattern matching, and tail recursion.

Erlang does have if and case statements, and they too rely heavily on the pattern matching idiom.  It is worth nothing that pattern matching in Erlang is simply an alternate way to express if statements, and are done sequentially.  No XSLT style weighting heuristics nor Prolog style backtracking is performed.

At the top of source files you will typically find a series of declarations which start with a -.  This fills the role of C # pragmas, and may be used to define the name of the module, list what is exported, include other files, and the like.

Erlang has a number of built in functions (BIFs to use the lingo) which do various things like map and filter.  The nearest equivalent of lambda is funspawn is used to create either local or remote processes.  Local processes are essentially green threads, though I suspect that there is heavy use of synchronous  I/O multiplexing going on behind the scenes.

Another BIF is c, which compiles Erlang code

Sending a message to another process is achieved via the ! operator.  receive is a statement built into the language that blocks the process until data is available, but otherwise is identical to a pattern matching case statement.  Analogous to C's idiom of having a break statement separating clauses in a switch statement, a common idiom in Elang is to terminate each receive clause with a tail recursion call back to the function which contains the receive statement.  After seeing this a few times, one quickly starts thinking of this as a form of goto instead of as a mind-bending form of recursion.  This isn’t as bad as it sounds.

The above should be enough to make the concurrency section of the Getting Started manual comprehensible.

Once that is mastered, one can move onto to Yaws, ErlyWeb and Mnesia (overview).