Erlang Concurrency

Erlang Concurrency

Concurrency is defined as “the temporal property of two things happening at the same time”. In Computer Science, the definition is “concurrency is a property of systems in which several computations are executing simultaneously, and potentially interacting with each other. The computations may be executing on multiple cores in the same chip, preemptively time-shared threads on the same processor, or executed on physically separated processors.”

Erlang was built with concurrency and fault-tolerance in mind.

Processes

The granularity of concurrency in Erlang is a process. A process is an activity/task that runs concurrently with and independently from the other processes (though processes can interact with each other using messages, links, etc.). Processes in Erlang are different than the processes and threads most people are familiar with. Erlang processes are lightweight, operate in (memory) isolation from other processes, and are scheduled by the Erlang’s Virtual Machine (VM). The creation time of process is very low, the memory footprint of a just spawned process is very small, and a single Erlang VM can have millions of processes running.

Messaging Passing

The communication model (among processes) in Erlang is message passing. Erlang processes share no memory. The way that processes communicate is via message passing (asynchronous). Every process (even the shell) holds a mailbox queue where incoming messages are placed until received by the process. Message passing is asynchronous because the sending process does not block on send. Sending a message in Erlang is a non-blocking operation that always succeed (more in the next post).

Why Message Passing?

We are so used to the shared memory model, why changing it? Here are some characteristics that are part of Erlang mostly because of the message passing memory model.

Crash Independency
Message passing allows for easier distributed programming. Imagine if you want to distribute an application that uses shared memory. To do this, one should either use a message passing solution (such as MPI) or a Distributed Shared Memory system (DSM), that also uses message passing to operate. Why not using message passing in the first place? Especially in Erlang, message passing allows for location transparency (when sending a message there is no difference to the programmer if the receiver resides in the local or a remote node).

Creating Processes

Erlang provides Built-In Functions that are used to spawn new processes. The simplest one is spawn/1|3 (the 1|3 denotes that both spawn/1 and spawn/3 functions exist).

Pid Datatype
Pid stands for Process identifier and is the datatype used for the unique process, identifiers that are assigned to every process.

spawn/1|3
Creates a new process and returns its pid. The new process is placed in the system scheduler queue, so it will be run some time later.

spawn/1
Called as spawn(Fun). The new process will run function Fun with an empty list ([]) as input.

spawn/3
Called as spawn(Module, Function, Args). The new process will run function Module:Function with the elements of the list Args as input.

Example:

process.erl
1
2
3
4
5
6
7
8
9
10
11
-module(process).
-export([start/0, say_something/2]).

say_something(_What, 0) -> done;
say_something(What, Times) ->
  io:format("~p~n", [What]),
  say_something(What, Times - 1).

start() ->
  spawn(process, say_something, ["hello", 3]),
  spawn(process, say_something, ["bonjour", 2]).
process1.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-module(process1).
-export([start/0, ping/2, pong/0]).

ping(0, Pong_Pid) ->
  Pong_Pid ! finished,
  io:format("ping finished~n", []);
ping(N, Pong_Pid) ->
  Pong_Pid ! {ping, self()},
  receive
    pong ->
      io:format("ping received pong~n", [])
  end,
  ping(N - 1, Pong_Pid).

pong() ->
  receive
    finished ->
      io:format("pong finished~n", []);
    {ping, Ping_Pid} ->
      io:format("pong received ping~n", []),
      Ping_Pid ! pong,
      pong()
  end.

start() ->
  Pong_Pid = spawn(message, pong, []),
  spawn(message, ping, [3, Pong_Pid]).

Creating Linked Processes

A very useful feature is to create a new process that is linked to the “parent” one. The link between them guarantees that if one of the two fails (finishes abnormally), the other one also stops executing. This feature is very helpful because it reduces the need for “cleaning up” in case of a failure. Instead of explicitely handling an error, the “let it fail and let someone else handle it” philosophy can be used. The BIF(s) providing this functionality are the spawn_link/1|3.

link/1
Creates a bidirectional link between the calling process and another process (or port), if there is not such a link already. If a process attempts to create a link to itself, nothing is done. Returns true.

spawn_link/1|3
Provides the same functionality as spawn/1|3 with the addition that a link is atomically created between the caller and the newly spawned process.

spawn_link/1
Same call convention as spawn/1.

spawn_link/3
Same call convention as spawn/3.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
spawnLink() ->
  spawn(fun spawnLink_/0).

spawnLink_() ->
  spawn_link(?MODULE, sayExit, []),
  justLoop().

justLoop() ->
  justLoop().

sayExit() ->
  timer:sleep(4000),
  erlang:kill(not_catched).

1> c(spawning).
{ok,spawning}
2> Pid = spawning:spawnLink().
<0.42.0>
3> erlang:is_process_alive(Pid).
true
4> erlang:is_process_alive(Pid).
true
5> erlang:is_process_alive(Pid).
=ERROR REPORT==== 7-May-2011::12:24:54 ===
Error in process <0.43.0> with exit value: {undef,[{erlang,kill,[not_
false
6> erlang:is_process_alive(Pid).
false

Other Processes’-related Built-In Functions

There are several other BIFs related to processes. The following are some commonly used.

is_pid/1
Returns true if the argument is a pid, else false.

1
2
3
4
5
1> Pid = spawn(io, format, ["Hello~n"]).
Hello
<0.37.0>
2> is_pid(Pid).
true

is_process_alive/1
Called as is_process_alive(Pid). Pid must refer to a process at the local node. Returns true if the process exists and is alive, that is not exiting and has not exited. Otherwise, returns false.

1
2
1> is_process_alive(self()).
true

list_to_pid/1
Transforms the input string to a pid. This BIF is intended to be used for debugging and not in the application development.

1
2
1> Pid == list_to_pid("<0.39.0>").
true

pid_to_list/1
Returns the textual representation of a pid. This BIF is intended to be used for debugging only.

1
2
1> StringPid = pid_to_list(Pid).
"<0.37.0>"

register/2
Registers a process (or a port) with a name. This name can be later used to refer to the process.

1
2
3
4
1> ShellPid = self().
<0.99.0>
2> register(shell, ShellPid).
true

registered/0
Returns a list with the names of all registered processes.

1
2
3
4
5
6
1> registered().
[init,shell,error_logger,rex,kernel_sup,inet_db,
 global_name_server,file_server_2,code_server,
 erl_prim_loader,user_drv,standard_error_sup,
 application_controller,standard_error,kernel_safe_sup,user,
 global_group]

self/0
One of the most commonly used BIF, returns the pid of the calling processes. As you will see in the next post (about messaging), self is used in almost every message send.

1
2
1> ShellPid = self().
<0.44.0>

erlang:send/2|3
Sends a message to a process. You will see message sending in detail in the next post.

erlang:send_after/3
Sends a message after a given amount of time.

unlink/1
Removes the link between two processes. Returns true even if there is no exist link.

unregister/1
Called as unregister(Name). Removes the association between the Name and the process which it is associated with.

1
2
3
4
5
6
7
8
1> unregister(shell).
true
2> registered().
[init,error_logger,rex,kernel_sup,inet_db,
 global_name_server,file_server_2,code_server,
 erl_prim_loader,user_drv,standard_error_sup,
 application_controller,standard_error,kernel_safe_sup,user,
 global_group]

whereis/1
Called as whereis(Name). Returns the pid of the process that is register with the name Name.

1
2
1> whereis(shell).
<0.44.0>

Erlang List Comprehension

Erlang List Comprehension

Syntax:

1
[Expression || Generators1, Guards1, Generators2, ...]

Expression
The expression specifies the elements of the result. Example: [I || <– [1, 2, 3]] returns the input list element as is.

Generators
Generators create the data used in ther filter-map operations. A generator has the “Pattern <– Data” format, where “Data” is a list or an expression that results to a list and Pattern is a pattern used to match with the elements of the list. This pattern can be used to disassembly elements. Example two valid generators are I <– lists:seq(1, 10) and {X, Y} <– [{‘A’, ‘Excellent’}, {‘B’, ‘Good’}, {‘C’, ‘Fair’}].

Guards
Guards are expression that return either true or false, the same as the guards we have seen in the previous posts. They apply to the variables that are on the left of the guard and the ones that are accessible to the scope where the comprehension runs. Example: I <– [1, 2, 3, 4], I rem 1 == 0 is a valid generator.

Example:

I will show some examples that implements some list functions. The most of them already exists in the lists module as I did in the past, I will add these functions to the module called mylists.

map/2
The results contains the elements of the input list after applied to the input function.

1
2
3
4
5
6
7
8
9
% Generator: the items of the list provided
% Guard: no guard expression, all items are kept
% Expression: the item from the generator after applied to the Function

map(Function, List) –>
  [Function(I) || I <– List].

1> mylists:map(fun(I) –> 2 * I end, [1, 2, 3, 4, 5]).
[2,3,6,8,10]

deleteall/2
Deletes all occurrences of an element from the list.

1
2
3
4
5
6
7
8
9
% Generator: the items of the list provided
% Guard: the item should not be equal (both value and type) with the Elem
% Expression: keep the elements of the list that "pass" the guard test, as they are

deleteall(Elem, List) –>
  [I || I <– List, I =/= Elem].

1> mylists:deleteall(3, [1, 2, 3, 4, 3, 2, 1]).
[1,2,4,2,1]

partition/2
Partition a list into two, according to if the elements satisfy or not a given predicate.

1
2
3
4
5
6
7
8
9
10
partition(Pred, List) –>
  {[I || I <– List, Pred(I)], [I || I <– List, not(Pred(I))]}.

% an alternative implementation
partition2(Pred, List) –>
    Sat = filter(Pred, List),
    {Sat, List  Sat}.

1> mylists:partition(fun(I) –> is_atom(I) end, [1, a, 2, b, 3.0]).
{[a,b],[1,2,3.0]}

replicated/2
Creates a list of Items of length Times.

1
2
3
4
5
6
7
8
% Generator: only used for fixing the length
% Expression: a fixed item

replicated(Item, Times) –>
  [Item || _ <– lists:seq(1, Times)].

1> mylists:replicated(':–)', 10).
[':–)',':–)',':–)',':–)',':–)',':–)',':–)',':–)',':–)',':–)']

replicate_items/2
Replicates each elements of the list Times times.

1
2
3
4
5
6
7
8
9
replicate_items(Times, List) –>
  mylists:flatten([[Item || _ <– lists:seq(1, Times)] || Item <– List]).

% same as
% replicate_items(Times, List) –>
%   mylists:flatten([replicated(Item, Times) || Item <– List]).

1> mylists:replicate_items(3, [a, b, c]).
[a,a,a,b,b,b,c,c,c]

member/2
Returns true if an element is a member of the list, else returns false.

1
2
3
4
5
6
7
member(Elem, List) –>
  [] /= [ok || I <– List, I == Elem].

1> mylists:member(a, [3, 2, 1, a, x, z]).
true
2> mylists:member(a, [3, 2, 1, aa, x, z]).
false

member_times/2
Returns the number of occurences of an element in a list.

1
2
3
4
5
6
7
8
9
member_times(Elem, List) –>
  length([ok || I <– List, I == Elem]).

1> mylists:member_times(a, [1, a, 2, 3, b, a, c]).
2
2> mylists:member_times(a, [1, a, 2, 3, b, c]).
1
3> mylists:member_times(a, [1, 2, 3, b, c]).
0

quicksort/1
This is the famous Quicksort implementation that is often used to show the power and compactness of Erlang.

1
2
3
4
5
6
7
8
9
qsort([]) –>
  [];
qsort([Pivot | List]) –>
  qsort([I || I <– List, I < Pivot])
  ++
  [Pivot | qsort([I || I <– List, I >= Pivot])].

1> mylists:qsort([7, 1, 3, 9, 1, 2, 0, 4, 6, 5]).
[0,1,1,2,3,4,5,6,7,9]

Multiple Generators
Now I will present some examples with multiple generators.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1> [{I, J} || I <– [1, 2, 3], J <– [a, b, c]].
[{1,a},{1,b},{1,c},{2,a},{2,b},{2,c},{3,a},{3,b},{3,c}]

% duplicate list
2> [I ||  <– [a, a], I <– [1, 2, 3]].
[1,2,3,1,2,3]

% duplicate elements
3> [I || I <– [1, 2, 3], J <– [a, a]].
[1,1,2,2,3,3]

% discard elements and duplicate the others
4> Discard = [2, 4].
[2,4]
5> [I || I <– [1, 2, 3, 4, 5, 6], not(lists:member(I, Discard)),  <– [a, a]].
[1,1,3,3,5,5,6,6]

% subsequences
8> [[I || I <– lists:seq(1, J)] || J <– lists:seq(1, 10)].
[[1],
 [1,2],
 [1,2,3],
 [1,2,3,4],
 [1,2,3,4,5],
 [1,2,3,4,5,6],
 [1,2,3,4,5,6,7],
 [1,2,3,4,5,6,7,8],
 [1,2,3,4,5,6,7,8,9],
 [1,2,3,4,5,6,7,8,9,10]]

Erlang List & List Module

Erlang Control Flow Statement

List is the the most important data type in Erlang, as in every functional programming language. In this article, I will present the Erlang’s lists module and its most important functions.

Syntax

1
[Element1, Element2, ..., ElementN]

N is called the length of the list. So, [] is the empty list.

Decomposing & Pattern Matching

An empty list pattern matches with [].

1
2
3
4
5
6
7
8
9
10
11
12
1> Empty = [].
[]
2> Empty == [].
true
3> Empty = [].
[]
4> NotEmpty = [1].
[1]
5> Empty == NotEmpty.
false
6> Empty = NotEmpty.
** exception error: no match of right hand side value [1]

A non-empty list pattern matches with [Head | Tail].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1> [Head | Tail] = [1, 2, 3, 4].
[1,2,3,4]
2> Head.
1
3> Tail.
[2,3,4]
4> [Head1 | Tail1] = Tail.
[2,3,4]
5> Head1.
2
6> Tail1.
[3,4]
7> [Head2 | Tail2] = [].
** exception error: no match of right hand side value []

Normal Representation
The format [Element1, Element2, …, ElementN] is a shorthand of [Element1 | [Element2 | … | [ElementN | []] … ] representation.
Example: the list [1, 2, 3] is a shorthand of [1 | [2 | 3 | []]], that is the normal representation of a list.

Decomposing:

1
2
3
4
5
6
7
8
9
10
1> [A | [B |[C | D]]] = [1, 2, 3].
[1,2,3]
2> A.
1
3> B.
2
4> C.
3
5> D.
[]

Composing:

1
2
3
4
5
6
7
8
1> List = [2,3].
[2,3]
2> List1 = [1 | List].
[1,2,3]
3> List2 = [-1 | [0 | List1]].
[-1,0,1,2,3]
4> List3 = [[-3, -2] | List2].
[[-3,-2],-1,0,1,2,3] % the head is just 1 element

Of course, since it is more readable and easier to write, the shorthand representation is usually used:

1
2
3
4
5
6
7
8
9
10
11
1> [A, B, C] = [1, 2, 3].
[1,2,3]
2> [A, B, C, D] = [1, 2, 3]. % does not match cause
% the left-hand side matches a 4-elements list
** exception error: no match of right hand side value [1,2,3]
3> [A, B, C | D] = [1, 2, 3].
[1,2,3]
4> D.
[]
5> [-3, -2 | [-1, 0 | [1 | [2,3]]]].
[-3,-2,-1,0,1,2,3]

List Parsing
The pattern matching you saw before can be used in a function in order to parse the list:

1
2
3
4
parse([]) ->
  parsed.
parse([Head | Tail]) ->
  parse(Tail).

Concatenation
Two lists can be concatenated using ++:

1
2
3
4
5
6
7
8
9
10
1> L1 = [1, 2], L2 = [3, 4, 5].
[3,4,5]
2> L1 ++ L2.
[1,2,3,4,5]
3> L1 ++ L2 ++ L1.
[1,2,3,4,5,1,2]
4> Mirror = fun(List) -> List ++ lists:reverse(List) end.
#Fun<erl_eval .6.13229925>
5> Mirror([a, b, {c}]).
[a,b,{c},{c},b,a]

Difference
You can take the difference of two lists (the left-hand side one without the element of the right-hand side) using the – operator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1> [1, 2, 3] -- [1, 2, 3].
[]
2> [1, 2, 3] -- [1, 3].
[2]
3> [1, 2, 3] -- [1, 3, 3].
[2]
4> [1, 3, 2, 3] -- [1, 3, 3].
[2]
5> [3, 1, 3, 2, 3] -- [1, 3, 3].
[2,3]
6> [1, 2, 3] -- [a, b].
[1,2,3]
7> Delete = fun(List, Element) -> List -- [Element] end.
#Fun<erl_eval .12.113037538>
8> Delete([1,2,3,4,1], 1).
[2,3,4,1]

Module Lists

The lists module defines some commonly used list processing functions. This module is extremely useful, so it is a good idea to “remember” what functions it provides.

all/2
Called as all(Pred, List). Returns true if Pred(Element) returns true for all lists’ elements.

1
2
3
4
5
6
7
8
9
10
1> L = [2, 4, 6, 8],
1> F = fun(X) -> X rem 2 == 0 end,
1> lists:all(F, L).
true
2> f().
ok
3> L = [2, 4, 5, 8],
3> F = fun(X) -> X rem 2 == 0 end,
3> lists:all(F, L).
false

append/1|2
Concatenates the lists to one.

1
2
3
4
1> lists:append([[1, 2], [3], [4, 5]]).
[1,2,3,4,5]
2> lists:append([1, 2], [3, 4]).
[1,2,3,4]

Notice that the operator ++ and the function append/2 are the same.

delete/2
Deletes an element from the list (first occurrence, if any).

1
2
1> lists:delete(a, [d, a, d, a, d]).
[d,d,a,d]

concat/1
Accepts a list of items (atom, integer, float, string) and returns the concatenation of their textual representation as a list.

1
2
1> lists:concat(["ab", '.', 1]).
"ab.1"

filter/2
Called as filter(Pred, List). Returns a list containing only the elements that return true for the Pred.

1
2
3
4
5
6
1> Gt10 = fun(X) -> X > 10 end, lists:filter(Gt10, [1, 2, 22, 3, 44, 5, 66]).
[22,44,66]
2> L = [1, a, b, 2, c, 3.0, d, {4}].
[1,a,b,2,c,3.0,d,{4}]
3> lists:filter(fun(X) -> is_number(X) end, L).
[1,2,3.0]

flatten/1
Returns a flattened (no element is a list) version of the input list.

1
2
1> lists:flatten([1, [2], [3, 4, [5, [6, 7]]], [[[[8]]]]]).
[1,2,3,4,5,6,7,8]

key*** functions
There are several functions which their name starts with the word “key”. They are all used to process lists of tuples.

1
2
3
4
5
6
7
8
9
10
1> Kl = [{a, k1, a}, {b, k2, b}, {c, k3, c}, {e, k5, e}].
[{a,k1,a},{b,k2,b},{c,k3,c},{e,k5,e}]
2> lists:keydelete(k3, 2, Kl).
[{a,k1,a},{b,k2,b},{e,k5,e}]
3> lists:keysearch(k3, 2, Kl).
{value,{c,k3,c}}
4> lists:keysearch(k4, 2, Kl).
false
5> lists:keyreplace(k3, 2, Kl, {new, tuple}).
[{a,k1,a},{b,k2,b},{new,tuple},{e,k5,e}]

last/1
Returns the last element of the list.

1
2
1> lists:last([1, 2, 3]).
3

map/2
Called as map(Fun, List). Applies function Fun to every item of the list and returns the resulting list.

1
2
1> lists:map(fun(I) -> 2 * I end, [1, 2, 3]).
[2,4,6]

partition/2
Partitions a list to two according to if the elements satisfy or not a given predicate.

1
2
1> lists:map(fun(I) -> 2 * I end, [1, 2, 3]).
[2,4,6]

reverse/1|2
Returns the reverse of a list.

1
2
3
4
1> lists:reverse([a, b, c, d]).
[d,c,b,a]
2> lists:reverse([a, b, c, d], [1, 2, 3]).
[d,c,b,a,1,2,3]

sort/1|2
Sorts a list to increasing order or according to a given function.

1
2
3
4
5
6
7
8
1> L = [3, 1, 2, 5, 4].
[3,1,2,5,4]
2> lists:sort(L).
[1,2,3,4,5]
3> Gt = fun(I, J) -> I > J end.
#Fun<erl_eval .12.113037538>
4> lists:sort(Gt, L).
[5,4,3,2,1]

sum/1
Returns the sum of the elements of a list containing numbers.

1
2
1> lists:sum([1, 2.2, 3]).
6.2

u*** functions
There are several function which their name starts with “u” and the results they return contain no duplicates.

1
2
1> lists:usort([5, 3, 2, 3, 2, 1, 4, 5]).
[1,2,3,4,5]

Erlang Control Flow Statement

Erlang Control Flow Statement

As we saw in the previous post, pattern matching with different function clauses can be used in order to control the execution flow in Erlang. Erlang also provides the if, case, and receive control flow constructs that can be used in a function body. In this post I will only present the if and casestatements since receive is used for message passing and I will write a dedicated post about the subject. Both if and case are similar to the statements of other programming languages.

If Statement

Syntax:

1
2
3
4
5
6
7
8
9
if
  BoolbeanExpression1 ->
    IfBody1;
  BooleanExpression2 ->
    IfBody2;
    ...
  true ->
    BodyCathAll
end

The different clauses, except the last one are like “else if” in other languages, while the last one (true ->) is like the “else”; it succeeds when all the previous clauses have failed.

Example:

1
2
3
4
5
6
7
is_greater_than(X, Y) ->
  if
    X > Y ->
      true;
    true ->
      false
  end

Case Statement

Syntax:

1
2
3
4
5
6
7
8
case Expression of
  Value1 [when Guard1] ->
    CaseBody1;
  Value2 [when Guard2]->
    CaseBody2;
  _Other ->
    CaseBodyCatchAll
end

Notice that the last clause (_Other) is like the default clause in other programming languages. The Expression should always return a value (if it is a function call) that will be used to perform the pattern matching.

Example:

1
2
3
4
5
6
7
8
9
check(List) when is_list(List) ->
  case lists:reverse(List) of
    List ->
      true;
    _ ->
      false
    end;
check(_) ->
    {error, arg_not_list}.

Erlang Tail Recursion

Erlang Variables

A function is called tail recursive when the recursive call to itself happens only in the last expression of the body in every clause.

Syntax:

1
2
3
4
5
6
7
8
non_tail_recursive(...) ->
  non_tail_recursive(...),
  other_expression,
  ... .
tail_recursive(...) ->
  other_expression,
  ...
  tail_recursive(...).

The first function is not the tail recursive as the recursive call is followed by other expression. While the second is, since the recursive call is the last statement.

Tail Recursion & Performance
In many programming languages tails recursion is a good approach performance. But in general, it is not the case in the latest releases of Erlang. Tail recursion is not guaranteed to give you better performance.

Tail Recursion VS Non Tail Recursion

Non Tail Recursive:

1
2
length([]) -> 0;
length([_ | T]) -> 1 + length(T).

Tail Recursive:

1
2
3
4
length(List) -> length(List, 0).

length([], L) -> L;
length([_ | T], L) -> length(T, L + 1).

Erlang Recursion

Erlang Variables

The definition of the word Recursion is “(mathematics) an expression such that each term is generated by repeating a particular mathematical operation”. Recursion is one of the most powerful “tools” in a functional programming language.

Recursion can be used to divide techniques to problem solving, where a problem is broken to smaller subproblems, the subproblems are solved, and the results are “merged” to generate the final result.

Recursion happens when a function calls itself directly or indirectly. Recursion is used instead of the conventional loop statements of other programming languages, such as while and for in C.

Syntax:

1
2
3
4
functionA(...) ->
  Body_before_recursion, % optional
  functionA(...),
  Body_after_recursion. % optional

Example:

example.erl
1
2
3
4
5
6
7
-module(example).
-export([sum/1]).

sum(1) -> 1;
sum(N) -> N + sum_n(N - 1).

%% example:sum(10).
example.erl
1
2
3
4
5
6
7
-module(example).
-export([fac/1]).

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

%% example:fac(10).
example.erl
1
2
3
4
5
6
7
-module(example).
-export([list_sum/1]).

list_sum([]) -> 0;
list_sum([H|T]) -> H + sum_list(T).

%% example:list_sum([1, 2, 3, 4, 5]).
example.erl
1
2
3
4
5
6
7
-module(example).
-export([list_increase_element/1]).

list_increase_element([]) -> [];
list_increase_element([H|T]) -> [H + 1|increase(T)].

%% example:list_increase_element([1, 2, 3, 4, 5]).
example.erl
1
2
3
4
5
6
7
-module(example).
-export([list_length/1]).

list_length([]) -> 0;
list_length([_H|T]) -> 1 + list_length(T).

%% example:list_length([1, 2, 3, 4, 5]).

Erlang Guards

Erlang Variables

Guard structures

Guards in Erlang are boolean functions placed after the key word, “when” and before the arrow, “->”. Guards may appear as part of a function definition, ‘receive’, ‘if’, ‘case’ and ‘try/catch’ expressions.

We use a guard in a function definition.

Example:

example.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-module(example).
-compile(export_all).

the_answer_is(N) when N =:= 42 -> true;
the_answer_is(N) -> false.

% c(example).
% ok
%
% example:the_answer_is(42).
% true
%
% example:the_answer_is(21).
% false

fun definition

1
2
3
4
F = fun
  (N) when N =:= 42 -> true;
  (N) -> false
end.

receive expression

1
2
3
4
receive
  {answer, N} when N =:= 42 -> true;
  {answer, N} -> false
end.

if expression

1
2
3
4
if
  N =:= 42 -> true;
  true -> false
end.

case expression

1
2
3
4
case L of
  {answer, N} when N =:= 42 -> true;
  _ -> false
end.
1
2
3
4
case L of
  {node, N} when N =:= 42 -> true;
  _AnyNode -> false
end.

try/catch

1
2
3
4
5
6
7
8
try find(L) of
  {answer, N} when N =:= 42 -> true;
  _ -> false
catch
  {notanumber, R} when is_list(R) -> alist;
  {notanumber, R} when is_float(R) -> afloat
  _ -> noidea
end.

Multiple Guards

It is possible to use multiple guards within the same function definition or expression. When using multiple guards, a semicolon, “;”, signifies a boolean “OR”, while a comma, “,”, signifies boolean “AND”.

Example:

1
2
the_answer_is(N) when N == 42, is_integer(N) -> true;
geq_1_or_leq_2(N) when N >= 1; N =< 2 -> true;

Guard Functions

There are several built-in-functions which be used in a guard.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
is_alive/0
is_boolean/1
is_builtin/3
is_constant/1
is_float/1
is_function/2
is_function/1
is_integer/1
is_list/1
is_number/1
is_pid/1
is_port/1
is_record/3
is_record/2
is_reference/1
is_tuple/1

tuple_size/1
is_binary/1
is_bitstring/1
bit_size/1
byte_size/1
length(Z) > N
1
2
3
4
5
6
7
8
A > B
A < B
A == B
A =< B
A >= B
A /= B
A =:= B
A =/= B

Erlang Functions

Erlang Variables

As you know by now, Erlang is a functional programming language. In my point of view, different programming has different problem solving philosophy:
- Procedural: describe the steps needed to be taken to solve the problem.
- Logical (Declarative): describe the problem properly and let the language solve it.
- Object-orientation: design the objects that will lead you to the solution.
- Functional: define small and precise functions that all together solve the problem.

Declaring a Function

Syntax:

1
2
3
4
function_name(Argument1, Argument2, ...) ->
  Statement1,
  Statement2,
  ...

Example:

example.erl
1
2
3
4
-module(example).
-export([double/1]).

double(N) -> 2 * N.

Erlang Modules and Compiling

Erlang Variables

Erlang code is divided into modules. A module consists of a sequence of attributes and function declarations, each terminated by period (.). It provided the contained functions with a common namespace as well, You can imagine a module as a package in Java, or a header file in C. Program in Erlang spans over more than one modules.

Example:

m.erl
1
2
3
4
5
6
7
-module(m).          % module attribute
-export([fact/1]).   % module attribute

fact(N) when N>0 ->  % beginning of function declaration
  N * fact(N-1);     %  |
fact(0) ->           %  |
  1.                 % end of function declaration

Defining Modules

Assume that we want to create a module that will contain our own implementation of list functions and name it mlists.
First, we create a file named mlists.erl that will contain the module. The container file and the module names have to be the same.

Calling a Module’s Function

The calling function of a module, convention in Erlang is module:function(argument1, argument2, ...).

Example:

1
2
1> lists:max([1,3,2]).
3

Calling a Module Function within the Module

A function defined within the module file can be called either as module_name:function(arguments1, argument2, ...), or function(arguments1, argument2, ...), so the module name can be skipped.

Modules Attributes

Then we need to define the module’s attributes. An attribute is the information that we provide to the Erlang compiler. It is placed as a directive (usually) in the top of the file and has the -attribute_name(attribue_value(s)). format. The one attribute that we have to define is the one providing the module name.

-module(Module).
Module declaration, defining the name of the module. The name Module, an atom, should be the same as the file name minus the extension erl. Otherwise code loading will not work as intended.

Example in the top of mlists.erl place:

mlists.erl
1
-module(mlists).

-export(Functions).
Exported functions is used to define which functions the module exports, where “exports” means that they will be available to be called outside the module.

All the functions that are not exported by the module are only visible within the file, similar with the private functions in Ruby, Java and the static ones in C.

Example:

mlists.erl
1
2
-module(mlists).
-export([function1/arity1, function2/arity2, ...]).

-import(Module,Functions).
You can use this directive in order to import the selected exported functions of a module in the namespace of another one. It means that if you do so, you will be able to call the functions without the module prefix. Although in some cases it could be convenient, it is not recommended to use this directive, because it decreases the code’s readability.

-compile(Options).
Compile is used to pass compilation instructions to the compiler.

Example:

1
-compile([export_all]).

-on_load(Function).
Names a function that should be run automatically when a module a loaded.

Compiling Modules

Erlang programs must be compiled to object code. The compiler can generate a new file which contains the object code. The current abstract machine which runs the object code is called BEAM. To compile the modules start an Erlang emulator on the folder that contains your source files. In order to compile a .erl, using c(Module) Bult-in Function.

Example:

1
2
1> c(mlist).
{ok, mlist}

If there is no error occurs, the compiler generates the compiled .beam file.

Loading a Module

The object code must be loaded into the Erlang runtime system. To load the modules start an Erlang emulator on the folder that contains your source files. In order to load a compiled module .beam, using l(Module) Bult-in Function.

1
2
1> l(mlist).
{module, mlist}

I promise, from now on the posts will be far more interesting. Next one, or two posts will be about defining functions in Erlang. You can imagine how important functions are for a functional programming language. see ya! :)

Erlang Datatypes

Erlang Variables

I will introduce the most datatypes that being used in Erlang such as number, atom, function, tuple, map, list, record, and boolean.

Number
There are two datatypes of numeric are integers and floats.

Example:

1
2
3
4
1> 42.
42
2> 2.3.
2.3

Atom
Atom is a literal, a constant with name. Atom should be enclosed in single quotes (‘) if it does not begin with a lower-case letter or if it contains other characters than alphanumeric characters, underscore (_), or @ sign.

Example:

1
2
3
4
hi
phone_number
'Sunday'
'phone number'

Fun
Fun is a functional object. Funs make it possible to create an anonymous function and pass the function itself.

Example:

1
2
3
4
1> Fun1 = fun (X) -> X+1 end.
#Fun<erl_eval.6.39074546>
2> Fun1(6).
7

Tuple
Tuple is a compound data type, it consists of elements of any datatypes.

Syntax:

1
{Element1, Element2, ..., ElementN}

Example:

1
2
3
4
5
6
7
8
9
10
> T = {bunlong, 27, {may, 17}}.
{bunlong, 27, {may, 17}}
> element(1, T).
bunlong
> T2 = setelement(2, T, 25).
{bunlong, 25, {may, 17}}
> tuple_size(T).
3
> tuple_size({}).
0

Map
Map is a compound data type with a variable number of key-value associations.

Syntax:

1
#{Key1=>Value1,...,KeyN=>ValueN}

Example:

1
2
3
4
5
6
7
8
9
10
11
12
1> M1 = #{name=>bunlong,age=>26,date=>{may,07}}.
#{age => 26,date => {may,07},name => bunlong}
2> maps:get(name,M1).
bunlong
3> maps:get(date,M1).
{may,07}
4> M2 = maps:update(age,27,M1).
#{age => 27,date => {may,07},name => bunlong}
5> map_size(M).
3
6> map_size(#{}).
0

List
As in all functional programming language, list is one of the most used datatyped. Again, Erlang borrows the list syntax from Prolog. Because of their importance. List is a compound data type with a variable number of terms.

Syntax:

1
[Element1, Element2, ..., ElementN]

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> L = [a, 2, {c, 4}].
[a, 2, {c, 4}]
> [H|T] = L.
[a, 2, {c, 4}]
> H.
a
> T.
[2, {c, 4}]
> L2 = [d|T].
[d, 2, {c, 4}]
> length(L).
3
> length([]).
0

Record
A record is a data structure for storing a fixed number of elements. It has named fields and is similar to a struct in C. However, record is not a true data type.

Example:

1
2
3
4
5
6
7
8
-module(person).
-export([new/2]).
-record(person, {name, age}).
new(Name, Age) ->
  #person{name=Name, age=Age}.

% > person:new(bunlong, 27).
% {person, bunlong, 27}

Boolean
There is no Boolean data type in Erlang. Instead the atoms true and false are used to denote Boolean values.

Example:

1
2
3
4
> 2 =< 3.
true
> true or false.
true

So are so good, there are some other datatypes, such as binary, reference, Pid, etc. I will explain them when needed. see you in the next articles! :)