## Introduction

Erlang

Background

Sequential Erlang (basics)

Concurrent Erlang (basics)

## Prominent applications

- Telecoms
- Switches (POTS, ATM, IP, ...)
- GPRS
- SMS applications

- Internet applications
- Whatsapp (article, another one)
- Klarna (online shopping)
- Formerly Facebook (chat)

- 3D modelling (Wings3D)

## History lesson

- 1981 – Ericsson CSLab formed
- 1986 – Prolog “games”
- 1987 – Erlang name appears
- 1989 – Prototypes show 9–22 fold increase in design efficiency
- 1995 – AXE-N fails after 8 years
- 1998 – AXD301 delivered
- 1998 – Erlang banned; goes open source
- 2007 – Ericsson uses Erlang for new products

## Essence

- Shared-nothing approach—isolated processes
- Each process runs a sequential program
- Sequential subset is functional
- Dynamically typed
- No shared memory
- Open Telecom Platform libraries
- Practically “proven” programming patterns
- Utilities

- A few important things done right
- A few less important things done wrong

## Sequential aspects

We cover the following aspects. Click on the links for further information.

Data types and variables (Erlang manual)

Function definitions (Erlang manual)

Pattern matching (Erlang manual)

Evaluation strategy

Tail recursion (Learn some Erlang for Great Good!)

## Expressions

`3 + 5`

`3`

and`5`

are integers

`true orelse false`

`true`

and`false`

represent boolean values`not`

,`andalso`

and`orelse`

are the standard (short-circuiting) boolean operators

Erlang is dynamically-typed, which means that expressions like

`4 + true`

are well-formed, and fail only at runtime

## Interpreter

$ erl Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] Eshell V7.0 (abort with ^G) 1> 3 + 5. 8 2>

Erlang interpreter allows for evaluating expressions

Each expression must be terminated with a

`.`

(fullstop)

## Evaluation of expressions

`3 + `

→ __(2 * 4)__

→ __3 + 8__`11`

Evaluation continues until a

*value*is reachedIntegers and booleans are values

Operators applied to expressions are not values

## Precedence

Task: You want to check how is the expression `? andalso ? orelse ?`

decoded: is it `(? andalso ?) orelse ?`

or `? andalso (? orelse ?)`

? In other words, is the precedence of `andalso`

higher than that of `orelse`

?
Design an experiment that will confirm one or the other.

Additional question: can the experiment really confirm it?

## Functions

% This is a comment g(N) -> N + 2.

`g`

is the function name`N`

is a varable—variables start with a capital letter`N + 2`

is the body of the function

## Evaluation of functions

1> g(3) * g(1). 15 2>

→ __g(3)__ * g(1)

→ __(3 + 2)__ * g(1)`5 * `

→ __g(1)__`5 * `

→ __(1 + 2)__

→ __5 * 3__`15`

The rules are the same as in Java

The difference is that we are not guaranteed that

`g(3)`

will be evaluated before`g(1)`

In practice the left-to-right evaluation order is used, but we shouldn't rely on it

Exception: short-circuiting

`andalso`

and`orelse`

operators do have a defined order of evaluation

## Multiple clauses

h(2) -> 4; h(3) -> 6; h(N) -> N - 1.

Functions can have multiple clauses

Each clause has a pattern (here:

`2`

,`3`

and`N`

)When a function is called, its arguments are matched against the patterns, starting with the first clause

The first clause whose pattern matches the argument will be taken

For example

`h(3)`

will return`6`

, as the second caluse will be chosen

## Repeated variables

myequal(N, N) -> true; myequal(N, M) -> false.

- Repeated variable must match the same value in all occurrences

## Guards

h2(1) -> 1; h2(N) when N < 5 -> 3; h2(N) -> N + 1.

A guard is a boolean expression associated with a clause

The clause is taken only when the guard evaluates to

`true`

Only a limited subset of expressions is allowed (no function calls, except for some builtin functions)

## Recursion

sum_m_n(M, N) when M > N -> 0; sum_m_n(M, N) -> M + sum_m_n(M+1, N).

A function is recursive if it calls itself (directly or indirectly)

Recursion is the way to implement loops in Erlang

`sum_m_n(M, N)`

takes two integers and computes the sum of numbers from`M`

to`N`

inclusive

__sum_m_n(2, 4)__

→

`2 + `

__sum_m_n(3, 4)__

→

`2 + (3 + `

__sum_m_n(4, 4)__)

→

`2 + (3 + (4 + `

__sum_m_n(5, 4)__))

→

`2 + (3 + (`

__4 + 0__))

→

`2 + (`

__3 + 4__)

→

__2 + 7__

→

`9`

## Tail-recursion

% The same function reimplemented using tail-recursion sum_m_n_i(M, N, Acc) when M > N -> Acc; sum_m_n_i(M, N, Acc) -> sum_m_n_i(M+1, N, Acc + M). % We define a convenience function sum_m_n_i(M, N) -> sum_m_n_i(M, N, 0).

A recursive call is a tail call if it is the last thing to be evaluated in the given body of the function

Tail-recursive functions can be used to implement accumulating loops

Sometimes tail-recursion is preferred for efficiency

Functions with the same name and different numbers of arguments can be defined (they are treated as different functions)

__sum_m_n_i(2, 4)__

→

__sum_m_n_i(2, 4, 0)__

→

`sum_m_n_i(3, 4, `

__0 + 2__)

→

__sum_m_n_i(3, 4, 2)__

→

`sum_m_n_i(4, 4, `

__2 + 3__)

→

__sum_m_n_i(4, 4, 5)__

→

`sum_m_n_i(5, 4, `

__5 + 4__)

→

__sum_m_n_i(5, 4, 9)__

→

`9`

- In this case we exploit the fact that it does not matter in which direction we add numbers

## Multiple statements, variables

f(X) -> X * 2. g(N) -> Y = f(N-1), Y + 2.

Comma separates multiple statements executed in a sequence

A statement is either an expression or an assignment

An assignment

`X = E`

has a pattern on its right-hand side and an expression on the left-hand sideIf the result of evaluating the expression matches the pattern, then the variables of the pattern are assigned values

Otherwise, the assignment fails (runtime error)

Variables that are bound (are assigned values) are treated as ordinary values in a pattern

__g(5)__

→

`Y = f(`

__5-1__),

`Y + 2`

→

`Y = `

__f(4)__,

`Y + 2`

→

`Y = `

__4 * 2__,

`Y + 2`

→

`8 + 2`

→

`10`

## Data types

Numbers

- Integers (arbitrarily big)
- Floats

Atoms

start_with_a_lower_case_letter ’Anything_inside_quotes\n\09’

Tuples

{} {atom, another_atom, ‘PPxT’} {atom, {tup, 2}, {{tup}, element}}

- Lists
[] [1, true] [a, b, c] = [a | [b, c]] = [a | [b | [c]]] = [a | [b | [c|[]]]]

- Lists have two primitive constructors
`[]`

: empty list`[X | Y]`

: non-empty list, consisting of the first element (head) and the rest of the list (tail)

- Lists also have a convenience notation
`[A, B, C, D]`

- Another notation is also available:
`[A, B | C]`

, which is the same as`[A | [B | C]]`

- Lists have two primitive constructors
- Strings
"This is a string"

- Strings are just list of numbers (ASCII codes) having a convenience notation
- Lists are printed as strings when all numbers that they contain correspond to printable characters
- Both inconvenient and slow:(

## Modeling data

- Complex data types are typically created using tuples decorated with atoms

{shape, {triangle, 2, 2, 3}, blue} {shape, {square, 3}, red} {shape, {rectangle, 3, 4}, white}

- Algebraic data types can be represented in this way

data Tree a = Leaf a | Node (Tree a) (Tree a)

edoc style “types”

*Atoms*to indicate which constructor is used at top-level*Tuples*to collect the arguments of the constructor

Example:

Node (Node (Leaf 3) (Leaf 4)) (Leaf 5)

becomes{node, {node, {leaf,3}, {leaf, 4}}, {leaf, 5}}

## Pattern matching

Function

`area`

to compute the area of different geometric figures.area({square, Side}) -> Side * Side; area({circle, Radius}) -> Radius*Radius*math:pi().

Patterns:

`{square, Side}`

and`{circle, Radio}`

`{square, Side}`

matches`{square, 4}`

and binds`4`

to variable`Side`

`{circle, Radius}`

matches`{circle, 1}`

and binds`1`

to variable`Radius`

## More pattern matching

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

- Succeeds: binds
`B`

to`10`

,`C`

to`foo`

and`D`

to`bar`

- Succeeds: binds
{A, A, B} = {abc, abc, foo}

- Succeeds: binds
`A`

to`abc`

,`B`

to`foo`

- Succeeds: binds
{A, A, B} = {abc, def, 123}

- Fails

[A,B,C,D] = [1,2,3]

- Fails

## Even more pattern matching

[H|T]= [1,2,3,4]

- Succeeds: binds
`H`

to`1`

,`T`

to`[2,3,4]`

- Succeeds: binds
[H|T] = [abc]

- Succeeds: binds
`H`

to`abc`

,`T`

to`[]`

- Succeeds: binds
[H|T] = []

- Fails

{A, _ , [ B | _ ] , {B} } = {abc,23,[22,x],{22}}

- Succeeds: binds
`A`

to`abc`

,`B`

to`22`

- Succeeds: binds

## Consuming lists

Computing the length of a list

len([X|XS]) -> 1 + len(XS); len([]) -> 0.

Note: `[a, b, c]`

is the same as `[a | [b, c]]`

__len([a, b, c])__

→

`1 + `

__len([b, c])__

→

`1 + (1 + `

__len([c])__)

→

`1 + (1 + (1 + `

__len([])__))

→

`1 + (1 + (`

__1 + 0__))

→

`1 + (`

__1 + 1__)

→

__1 + 2__

→

`3`

The function is defined recursively (inductively)

- Base case: empty list (
`[]`

) - Recursive case: a list with at least one element (
`[X | XS]`

)

- Base case: empty list (
Tail recursive version

len_i([], Acc) -> Acc; len_i([X|XS], Acc) -> len_i(XS, Acc+1). len_i(XS) -> len_i(XS, 0).

__len_i([a, b, c])__

→

__len_i([a, b, c], 0)__

→

`len_i([b, c], `

__0 + 1__)

→

__len_i([b, c], 1)__

→

`len_i([c], `

__1 + 1__)

→

__len_i([c], 2)__

→

`len_i([], `

__2 + 1__)

→

__len_i([], 3)__

→

`3`

## List examples

We want to define different functions working on lists

> c(list_examples). {ok,list_examples} > list_examples:sum([1,2,3,4]). 10 > list_examples:len([0,1,0,1]). 4 > list_examples:append([5,4],[1,2,3]). [5,4,1,2,3]

# Adding elements of a list

Simplest case (base case)

sum([]) == 0

The inductive case

- Focus on an example
sum([X1,X2,X3,X4]) == X1 + X2 + X3 + X4

We should try to rewrite this equation so that it appears the head of the list (

`X1`

) and the rest of the list (`[X2,X3,X4]`

) applied to`sum`

. Let us associate the sum as follows.sum([X1,X2,X3,X4]) == X1 + (X2 + X3 + X4)

We know that

`X2 + X3 + X4`

is equal to`sum([X2,X3,X4])`

. After all, this is the function that we want to define.We can then rewrite the equation above as:

sum([X1,X2,X3,X4]) == X1 + sum([X2,X3,X4])

- We generalize the last equation for the case when the
list has a head element
`X`

and the rest of the list is`XS`

sum([X | XS]) == X + sum(XS)

- We can now define the
`sum`

function as follows.

sum([X|XS]) -> X + sum(XS); sum([]) -> 0.

Why is the base case as second clause?

- We generalize the last equation for the case when the
list has a head element

- Focus on an example

## More examples

Computing the length of a list

len([X|XS]) -> 1 + len(XS) ; len([]) -> 0.

- Observe that
`X`

is not used on the right-hand side. It can be replaced by`_`

len([_|XS]) -> 1 + len(XS) ; len([]) -> 0.

- Observe that
Append elements to the end of a list

append([X|XS],YS) -> [X | append(XS,YS) ] ; append([], YS) -> YS.

## Side effects

Each statement may have side effects

f() -> io:format("line 1~n"), io:format("line 2~n"), io:format("line 3~n").

## Case statements

Function

`area()`

can be rewritten using a case statementarea({square, Side}) -> Side * Side; area({circle, Radius}) -> Radius*Radius*math:pi().

area(X) -> case X of {square, Side} -> Side * Side; {circle, Radius} -> Radius*Radius*math:pi() end.

__area({square, 3})__

→

__case {square, 3} of__

__ {square, Side} -> Side * Side;__

__ {circle, Radius} -> Radius*Radius*math:pi()__

__end__

→

__3 * 3__

→

`9`

- Case statements can also have guards

## If statements

f(X) -> if X > 2 -> X + 2; true -> X - 2 end.

- Limited version of a case statement, where only guards are used

## Modules

Basic compilation unit is a module

- Module name = file name (.erl)

Modules contain function definitions

- Like
`factorial`

and`area`

(see above) - Some functions can be exported to be usable from outside of the module

- Like
Let us create the module

`math_examples`

as follows.-module(math_examples). -export([factorial/1,area/1]). factorial(0) -> 1; factorial(N) -> N * factorial(N-1). area({square, Side}) -> Side*Side ; area({circle, Radio}) -> Radio*Radio*math:pi().

Running the examples.

> c(math_examples). {ok,math_examples} > math_examples:factorial(3). 6 > math_examples:area({square,4}). 16 > math_examples:area({circle,1}). 3.141592653589793

## Data types (Records)

- Definition
-record(person, {name = “”, phone = [], address}).

- Creation
X = #person{name = “Joe”, phone = [1,1,2], address= “here”}

- Accessing record fields
X#person.name X#person{phone = [0,3,1,1,2,3,4]}

## A note on the use of records and functions

Records are very convenient in several situations

Sometimes, functions require extra arguments but we realize about that later on

For instance, assume we would like to write the following error reporting function

debug(50) -> io:format("Item not found in the stock ~p",[]) ; debug(100) -> io:format("User not registered ~p",[]) ; debug(ErrCode) -> io:format("Unknown error ~p. Shutting down ~n",[ErrCode]).

Now, you would like to show part of your program state when an unknown error happens for debugging purposes

debug(50,_) -> io:format("Item not found in the stock ~p",[]) ; debug(100,_) -> io:format("User not registered ~p",[]) ; debug(ErrCode,State) -> io:format("Unknown error = ~p State = ~p ~n",[ErrCode, State]),

If the function

`debug`

had 100 cases, you need to modify 100 lines!Use records for those arguments that might get extended in the future!

-record(arg, { number }). debug(#arg{ number = 50}) -> io:format("Item not found in the stock ~p",[]) ; debug(#arg{ number = 100}) -> io:format("User not registered ~p",[]) ; debug(#arg{ number = ErrCode}) -> io:format("Unknown error ~p. Shutting down ~n",[ErrCode]).

If we want to add the state to print out, it is easy: modify the definition of

`arg`

and only the line that uses that extra information.-record(arg, {number, state}). debug(#arg{number = 50}) -> io:format("Item not found in the stock ~p",[]) ; debug(#arg{number = 100}) -> io:format("User not registered ~p",[]) ; debug(#arg{number = ErrCode, state = State}) -> io:format("Unknown error ~p. Shutting down ~n",[ErrCode, State]).

It is possible to refer to the whole register passed as argument

-record(arg, {number, state}). debug(#arg{number = 50}) -> io:format("Item not found in the stock ~p",[]) ; debug(#arg{number = 100}) -> io:format("User not registered ~p",[]) ; debug(Arg = #arg{number = ErrCode, state = State}) -> io:format("Unknown error ~p. Shutting down ~n",[ErrCode, State]), io:format("The function was called with the record ~p~n",[Arg]),

Pattern matching happens at the level of the argument (it is a record) and the record's components

This would be particularly helpful when doing the laboration CCHAT