two-argument version) is split into two clauses, separated by a semicolon. When factorial/2 is called, the actual parameters are tested against the patterns in each clause head in turn to find the first match, then the body (after the arrow) is evaluated. The value of the final expression in the body is the return value of the call; no explicit return statement is needed. Erlang is dynamically typed, so a call to factorial(“pancake”) will compile but will raise a runtime exception when it fails to match any clause. Tail-calls are optimized, so this code will run in constant space.
Lists are enclosed in square brackets (see Figure 1B). A 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 lowercase 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-assignment (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 nonempty 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’s” or closures) to be defined on the fly, passed as arguments, or returned from functions.
figure 1: example1.erl.
a
-module(example1).
-export([factorial/1, qsort/1, member/2, foldl/3, sum/1]).
Compute the factorial of a positive integer. factorial(N) when is_integer(N), N > 0 -> factorial(N, 1).
A helper function which maintains an accumulator. factorial( 1, Acc) -> Acc; factorial(N, Acc) when N > 1 -> factorial(N - 1, N Acc).
B
Return a sorted copy of a list. qsort([]) -> [];
qsort([Pivot | Xs]) -> qsort([X || X <- Xs, X < Pivot])
++ [Pivot]
++ qsort([X || X <- Xs, X >= Pivot]).
c Is X an element of a binary search tree? member(_, empty) -> false; member(X, {_, X, _}) -> true; member(X, {Left, Y, _}) when X < Y -> member(X, Left); member(X, {_, _, Right}) -> member(X, Right).
D
“Fold” a function across elements of a list, seeding % with an initial value.
e.g. foldl(F, A0, [A, B, C]) = F(C, F(B, F(A, A0))) foldl(_, Acc, []) -> Acc; foldl(F, Acc, [X | Xs]) ->
NewAcc = F(X, Acc), foldl(F, NewAcc, Xs).
Give the sum of a list of numbers. sum(Numbers) -> foldl(fun(N, Total) -> N + Total end, 0, Numbers).
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.
}
// Re-initialize counter to zero. public synchronized void reset() {
nextVal = 0;
}
}
concurrent erlang Let’s introduce concurrent Erlang by translating a small example from Java:
// A shared counter.
public class Sequence {
private int nextVal = 0;
// Retrieve counter and
// increment.
public synchronized int
getNext() {
return nextVal++;
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 pre-increment 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.
References:
Archives