Notes On Erlang

How To Be Able To Read Erlang In 15 Minutes

AuthorSebastien Pierre <sebastien@ivy.fr>
Date15-Jul-2007

TODO: What are "#" and "." in "Emp#employee.nam" Goals

Things to know about Erlang:

Of course, you already know that Erlang is designed for implementing soft real-time distributed parallel applications.

Reading Erlang

The values

First things to know: in Erlang, values are immutable, meaning that values cannot change. This is true for lists and variables (which can only be bound once).

Values are numbers:

0
1.0

strings (are atoms):

'HelloWorld'
"Hello World"

terms (it's just a name which value is itself, it's not a string nor a variable), which are lowercase (and are also atoms):

hello
hello_world

variables (they can reference values, and can only be assigned a value once !), which start with an UpperCaseLetter

Hello
Hello_world
HelloWorld

tuples:

{}
{0, 1, 2, 3}
{one, "two", 3}
{{0, 1, 2}, {one, two}, {"one", "two"}}

lists:

[]
[0, 1, 2, 3]
[one, "two", 3]
[[0,1,2], [one,two], ["one, "two"]]

functions:

fun(X) -> X+1, end

Expressions (and pattern-matching)

Expressions in Erlang are basically values or application of values to functions. However, there is one mechanism which is the core of Erlang: pattern matching.

A = 10

which would be an “assignment” is actually the “unifcation” of 10 with A (and then A is bound to 10).

{A, B, C} = {10, foo, bar}

this binds 10 to A, foo to B and bar to C. No surprise here.

{A, A, C} = {10, foo, bar}

would fail because it cannot unify the two values (but {10, 10, bar} would work).

additionally you can use the “magic_ (wildcard) coming from PROLOG in your expressions (to denote that you don't care binding a subset of your value).

{_,A,_,B} = {one,two,three,four}

meaning that you only care about the second and forth item of the tuple. This implies (of course) that you cannot reference the first and third values.

These examples work also with lists instead of tuples, but with lists, you can use a rest notation to do the unification:

[Head, Tail] = [0,1,2,3,4]

will fail, however

[Head|Tail] = [0,1,2,3,4]

will bind 0 to Head and [1,2,3,4] to tail.

Statements, operations and functions

Statements are always terminated by ., and , can be used to join them.

A = [1,2,3,4].
B = [1,2,3,4], 5.

means that A will be bound to [1,2,3,4] but B will be bound to 5.

Comments always start with a %

% Hello, World !

Functions are defined like that:

f_name(ARG1, ARG2, ARG3) -> ... .

The return value is the value to which your body evaluates.

Functions are called very naturally:

f_name(ARG1,ARG2,ARG3)

If you want to overload a function (using pattern matching expressions) you can use ; to join your declarations (function clauses). Note that order is important (first matching, first applied).

f_name(EXPR) -> ... ;
f_name(EXPR) -> ... ;
f_name(EXPR) -> ... .

here is an example:

fac(1) -> 1;
fac(N) -> N * fac(N-1).

Also, you can add predicates to guard your function clauses:

fac(N) when N > 0

however, guarding clauses will make them insensitive to ordering (TODO: I have to find out why…).

Functions can also be declared as values

F = fun() -> ... end

TODO: How to make the anonymous function recursive ?

Last but not least, functions are values, and can be applied at anytime using the apply function:

apply(module_name, function_name, [Arg1, Arg2, ...]).

More about operations

We've implicitly seen the "unification" operation (by the operator '='), but there are a couple of default operations available:

Control flow

Control flow in Erlang is all driven by pattern matching, and some more procedural-like operations are available:

case EXPR of
	PATTERN -> ... ;
	PATTERN -> ... ;
	PATTERN -> ...
end

is a better version of the switch/case construct.

if	EXPR -> ... ;
	EXPR -> ... ;
	EXPR -> ...
end

is the famous if/elif/else with implicit elif and else.

Records

Erlang offers something that is close to C structures, which is called records. A record is as follows:

-record(RECORDNAME, {FIELD1, FIELD2=VALUE, FIELD3})

for instance

-record(person,{first_name,last_name,age,sex})

will create a record named person with the fields first_name, last_name, age and sex.

You can “instanciate” (create) records by using the # operator:

Robert = #person{first_name="Robert",last_name="Smith",age=47,sex=male}

now you can access Robert's name easily, by using the record field access notation:

Robert#person.first_name

last but not least, when declaring records, you can use introspection functions:

record_info(fields, RECORD) -> [Field]
record_info(size, RECORD) -> Size

TODO: I did not manage to get this working

Structuring your code

Declarations (module name, import and export symbols) are like

-DECL(VALUE).

ex:

-module(bank_client).
-export([deposit/2, withdraw/2, balance/1]).

Symbols can be referenced by their absolute name, which is

MODULE:SYMBOL

ex:

io:format("Hello, World !").

Erlang Idioms

Syntactic stuff

Number on is the naming scheme: terms are lowercase while variables start with an uppercase:

term
Variable

Function declaration is pretty much like stating facts in PROLOG:

f(0) -> "odd"    ;
f(1) -> "even"   ;
f(X) -> f(X rem 2) .

Here, f(0) and f(1) are facts (the parameter is not a symbol, it is a value, implying that you will give the result

Idioms include automatic string concatenation:

-define(HELLO, "hello"
	" world !"
).

When you export a function from a module you have to explicitly state its arity (like hello/0 if hello takes no parameter, hello/1 if it takes 1, etc).

Thinking recursively

If you've never played with functional languages, you'll probably have a hard time with Erlang at first, because you have to think recursively.

Here are two common patterns:

double([H|T]) -> [2*H|double(T)];
double([]) -> [].

which maps a function that doubles every element to the list:

member(H, [H|_]) -> true;
member(H, [_|T]) -> member(H, T);
member(_, []) -> false.

which iterates on a list and stop at a specific condition.

List operations

TODO: Operations on lists (add, push, pop, subset, index, length, swap) TODO: List comprehension

Gotchas

Symbols
- Variables always start with an UpperCase - Globals (name → value, like -define(V,0)) are UPPERCASE - Parameters are UPPERCASE or CamelCase - Functions are lowercase - Unbound symbols (terms) are lowercase
Functions
- -export([f/1, f2]) means f1 and f2 are different functions - using when (guarded function) is not the same as doing overloading with pattern-matching.
Interpreter
- don't forget to end your statements by '.' - you cannot declare functions in the interpreter (!!)
Modules
- if you -export(M). then your file must be named M.erl - to use stuff from M you have to first to c(M).

Summary about Erlang

The language is immutable (you could say also purely functional), meaning that everything is passed by value, which also implies that everything that can be referenced is a value (this includes functions).

Erlang uses pattern matching on values everywhere, from resolving the body for a function call to the communication primitives (send and receive).

Impressions

Erlang documentation is difficult to find because it's scattered in many places, where the links are broken. It's difficult to get a good short introduction on the syntax and how to get started.

However, there are two very useful documents:

  • [Kickstarting Erlang][ERL-KICKSTART], which will you get started easily and fast.
  • [An Erlang Course][ERL-COURSE], which contains a good summary of the things you've got to know to read and write Erlang.

Pattern matching is impressive, especially when you look at constructs such as case and if, which really simplify a lot.

The way named parameters are passed ([{name:value},{name:value},...]) is a bit ugly. Erlang can be quite verbose when you have to carry state from one function to another (but this could be simplified using macros).

  • -
Erlang Cookbook wiki
Erlang Kickstart, Mike Williams, 2003 archive
Erlang Wikipedia Entry wikipedia
Erlang Programming Course web book