single vertical bar separates the first element from the rest of the list. If a list is used in a clause head pattern, it will match list values, separating them into their components. A list with a double vertical bar is a “list comprehension,” constructing a list through generator and filter expressions. A double-plus (++) concatenates lists.

Tuples (vectors) are enclosed in curly braces (see figure 1C). Tuples in patterns will extract components out of tuples that they match. Identifiers that start with an uppercase letter are variables; those that start in lower-case are atoms (symbolic constants such as enum values, but with no need to define a numerical representation). Boolean values are represented simply as atoms true and false. An underscore (_) in a pattern matches any value and does not create a binding. If the same fresh variable occurs several times in a pattern, the occurrences must be equal to match. Variables in Erlang are single-assign-ment (once a variable is bound to a value, that value never changes).

Not all list-processing operations can be expressed in list comprehensions. When we do need to write list-processing code directly, a common idiom is to provide one clause for handling the empty list and another for processing the first element of a non-empty list. The foldl/3 function shown in figure 1D is a common utility that chains a two-argument function across a list, seeded by an initial value. Erlang allows anonymous functions (“fun” or closures) to be defined on the fly, passed as arguments, or returned from functions.

Erlang has expressions that look like assignments but have a different semantics. The right-hand side of = is evaluated and then matched against the pattern on the left-hand side, just as when selecting a clause to match a function call. A new variable in a pattern will match against the corresponding value from the right-hand side.

CONCURREN T ERLANG

Let’s introduce concurrent Erlang by translating a small example from Java:

Sequence.java

// A shared counter. public class Sequence {

private int nextVal = 0;

// Retrieve counter and increment. public synchronized int getNext() {

return nextVal++;

public synchronized void reset() { nextVal = 0;

}

}

 

A sequence is created as an object on the heap, potentially accessible by multiple threads. The synchronized keyword means that all threads calling the method must first take a lock on the object. Under the protection of the lock, the shared state is read and updated, returning the preincrement value. Without this synchronization, two threads could obtain the same value from getNext(), or the effects of a reset() could be ignored.

Let’s start with a “raw” approach to Erlang, using the concurrency primitives directly.

sequence1.erl (raw implementation)

-module(sequence1).

-export([make_sequence/0, get_next/1, reset/1]).

Create a new shared counter. make_sequence() ->

spawn(fun() -> sequence_loop(0) end).

 

sequence_loop(N) -> receive

{From, get_next} ->
From {self(), N},
sequence_loop(N + 1);
reset ->
sequence_loop(0)
end.

Retrieve counter and increment. get_next(Sequence) ->

Sequence {self(), get_next}, receive

{Sequence, N} -> N end.

Re-initialize counter to zero. reset(Sequence) ->

Sequence reset.

The spawn/1 primitive creates a new process, returning its process identifier (pid) to the caller. An Erlang process, like a thread, is an independently scheduled sequential activity with its own call stack, but like an operating-system process, it shares no data with other processes—pro-

References:

http://www.acmqueue.com

Archives