Concurrent programming in Erlang

1.1. Sequential Programming

Program 1.1 computes the factorial of an integer.

-module(math1).
-export([factorial/1]).

factorial(0) -> 1;
factorial(N) -> N * factorial(N-1).

Functions can be interactively evaluated using a program called the shell. The shell prompts for an expression and then evaluates and prints any expression which the user enters, for example:

> math1:factorial(6).
720
> math1:factorial(25).
15511210043330985984000000

In the above > is the shell prompt. The remainder of the line is the expression entered by the user. The following line is the result of the expression evaluation.

How the code for factorial was compiled and loaded into the Erlang system is a local issue.

In our example, the function factorial has two defining clauses: the first clause is a rule for computing factorial(0), the second a rule for computing factorial(N). When evaluating factorial for some argument, the two clauses are scanned sequentially, in the order in which they occur in the module, until one of them matches the call. When a match occurs, the expression on the right-hand side of the -> symbol is evaluated, and any variables occurring in the function definition are substituted in the right-hand side of the clause before it is evaluated.

All Erlang functions belong to some particular module. The simplest possible module contains a module declaration, export declarations and code representing the functions which are exported from the module.

Exported functions can be run from outside the module. All other functions can only be run from within the module.

Program 1.2 gives an example of this.

-module(math2).
-export([double/1]).

double(X) ->
    times(X, 2).
times(X, N) ->
    X * N.

The function double/1 can be evaluated from outside the module, whereas times/2 is purely local, for example:

> math2:double(10).
20
> math2:times(5, 2).
** undefined function: math2:times(5,2) **

In Program 1.2 the module declaration -module(math2) defines the name of the module, and the export attribute -export([double/1]) says that the function double with one argument is to be exported from the module.

Function calls can be nested:

> math2:double(math2:double(2)).
8

Choice in Erlang is provided by pattern matching. Program 1.3 gives an example of this.

-module(math3).
-export([area/1]).

area({square, Side}) ->
    Side * Side;
area({rectangle, X, Y}) ->
    X * Y;
area({circle, Radius}) ->
    3.14159 * Radius * Radius;
area({triangle, A, B, C}) ->
    S = (A + B + C)/2,
    math:sqrt(S*(S-A)*(S-B)*(S-C)).

Evaluating math3:area({triangle, 3, 4, 5}) yields 6.0000 and math3:area({square, 5}) yields 25 as expected. Program 1.3 introduces several new ideas:

  • Tuples – these are used as place holders for complex data structures. We can illustrate this by the following dialogue with the shell:

code.f559d2bf9159844df6fc297d419d75d4c4337218 Here Thing is bound to the tuple {triangle, 6, 7, 8} – we say the value of Thing is a tuple of size 4 – it has four elements. The first element is the atom triangle, and the next three elements are the integers 6, 7 and 8.

  • Pattern matching – this is used for clause selection within a function. area/1 was defined in terms of four clauses. The query math3:area({circle, 10}) results in the system trying to match one of the clauses defining area/1 with the tuple {circle, 10}. In our example the third clause representing area/1 would match, and the free variable Radius occurring in the head of the function definition is bound to the value supplied in the call (in this case to 10).
  • Sequences and temporary variables – these were introduced in the last clause defining area/1. The body of the last clause is a sequence of two statements, separated by a comma; these statements are evaluated sequentially. The value of the clause is defined as the result of evaluating the last statement in the sequence. In the first statement of the sequence we introduced a temporary variable S.