Erlang Sequential Programming

Simple Integer Arithmetic

1> 2 + 3 * 4.
14

Base 16 and base 32 notation

1> 2#111 * 10#3.
21
2> 16#cafe * 32#sugar.
1577682511434
3> 36#z.  %% 36, 10 + 26, is the biggest
35

Variables

1> X = 1234567.
1234567
2> X.
1234567
3> X * X * X.
1881672302290562263
4> X = 1234.
** exception error: no match of right hand side value 1234

Floating-Point Numbers

1> 5 / 3.
1.66667
2> 4 / 2.
2.00000
3> 5 div 3.
1
4> 5 rem 3.
2
5> 4 div 2.
2

Boolean and Comparison

7> 5 =:= 5.0.
false
8> 5 == 5.0.
true
9> 5 /= 5.0.
false
10> 5 =/= 5.0.
true
11> true and false.
false
12> true or false.
true
14> true xor false.
true
15> not false.
true
16> 1 >= 1.
true
17> 1 =< 1.
true
18> 1 < false.
true
19> % number < atom < reference < fun < port < pid < tuple < list < bit string

Atoms

1> hello.
hello

Like define in C:

#define NULL 0

Atoms start with lowercase letters, followed by a sequence of alphanumeric chars or the _ or @ sign. Atoms can also be quoted with ' char, e.g. 'Name', '+', 'Hello', or 'a'

1> 'Name'.
'Name'
2> 'a'.
a
3> 'a' == a.
true

Tuples

Like struct in C:

struct point {
  int x;
  int y;
} P;
P.x = 10; P.y = 45;

In Erlang it is:

1> P = {10, 45}
{10,45}
2> Point = {point, 10, 45}.
{point,10,45}
3> {point, X, Y} = Point.
{point,10,45}
4> X.
10
5> Y.
45
6> {_, _, Y2} = Point.
{point,10,45}
7> Y2.
45

The number of elements is said to be the size of the tuple.

1> tuple_size({a, b, c, d, 20}).
5

2.10 Lists

1> L = [1, 3, 5].
[1,3,5]
2> ThingsToBuy = [{apples, 10}, {pears, 6}, {milk, 3}].
[{apples, 10},{pears,6},{milk,3}]
3> [H|T] = L.
[1,3,5]
4> H.
1
5> T.
[3,5]
6> f().
ok
7> [H1, H2 | T] = [1,3,5,7,9].
[1,3,5,7,9]
8> {H1, H2, T}
{1, 3, [5,7,9]}
9> [1, 2, 3] ++ [4, 5].
[1,2,3,4,5]
10> [1, 2, 3] -- [1, 2].
[3]
19> hd([1, 2, 3]).
1
20> tl([1, 2, 3]).
[2,3]
21> [2*N || N <- [1,2,3,4]].
[2,4,6,8]
22> [X || X <- [1,2,3,4,5,6,7,8,9,10], X rem 2 =:= 0].
[2,4,6,8,10]

built-in functions (BIFs) are usually functions that could not be implemented in pure Erlang.

Strings

There are no strings in Erlang. They are the list of integers.

1> Name = "Hello".
"Hello"
2> [83,117,114,112,114,105,115,101].
"Surprise"
3> [1,83,117,114,112,114,105,115,101].
[1,83,117,114,112,114,105,115,101]

Dollar syntax

1> I = $s.
115
2> [I-32,$u,$r,$p,$r,$i,$s,$e].
"Surprise"

Bit Syntax

1> Color = 16#F09A29.
15768105
2> Pixel = <<Color:24>>.
<<240,154,41>>
3> <<R:8, G:8, B:8>> = Pixel.
<<240,154,41>>
4> R.
240
5> <<R1:8, _/binary>> = Pixel.
<<240,154,41>>
6> <<R2:16, _/binary>> = Pixel.
<<240,154,41>>
7> R2.
61594
18> R2 = <<240,154>>.  % R2 is an integer, not a binary.
** exception error: no match of right hand side value <<240,154>>
20> is_binary(R2).
false
21> is_integer(R2).
true
19> <<R2:16>> = <<240,154>>.
<<240,154>>
8> binary_to_list(R2).
[240,154]

**More Binary Stuff**
1> <<Port1:16/integer-signed-big>> = <<1,2>>.
<<1,2>>
2> Port1.
258
3> <<Port2:16/integer-signed-little>> = <<1,2>>.
<<1,2>>
4> Port2.
513

Type: integer | float | binary | bytes | bitstring | bits | utf8 | utf16 | utf32 Signedness: signed | unsigned Endianness: big | little | native

The standard binary operations (shifting bits to left and right, binary 'and', 'or', 'xor', or 'not') also exist in Erlang. Just use the functions bsl (Bit Shift Left), bsr (Bit Shift Right), band, bor, bxor, and bnot. e.g. 2#00100 = 2#00010 bsl 1.

One could parse TCP segments with code like this:

<<SourcePort:16, DestinationPort:16,
  AckNumber:32,
  DataOffset:4, _Reserved:4, Flags:8, WindowSize:16,
  CheckSum: 16, UrgentPointer:16,
  Payload/binary>> = SomeBinary.

Binary Comprehensions

1> [ X || <<X>> <= <<1,2,3,4,5>>, X rem 2 == 0].
[2,4]
2> Pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
3> RGB = [{R,G,B} || <<R:8,G:8,B:8>> <= Pixels].
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
4> << <<R:8,G:8,B:8>> || {R,G,B} <- RGB >>.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
5> << <<(X+1)/integer>> || <<X>> <= <<3,7,5,4,7>> >>.
<<4,8,6,5,8>>

Via: http://learnyousomeerlang.com/starting-out-for-real#bit-syntax

Get Help

$ erl -man lists

Chapter 3 Sequential Programming

3.1 Modules

Modules are the basic unit of code in Erlang. All the functions we write are stored in modules.

-module(geometry).
-export([area/1]).
area({rectangle, Width, Ht}) -> Width * Ht;
area({square, X}) -> X * X;
area({circle, R}) -> 3.14159 * R * R.

Usage

$ erl
1> c(geometry).
{ok,geometry}
2> geometry:area({rectangle, 10, 5}).
50
3> geometry:area({circle, 1.4}).
6.15752

3.3 Functions with the same name and different Arity

sum(L) -> sum(L, 0).

sum([], N) -> N;
sum([H|T], N) -> sum(T, H+N).

3.4 Funs

funs are anonymous functions.

1> Z = fun(X) -> 2 * X end.
#Fun<erl_eval.6.34545567>
2> Z(2).
4
3> Double = Z.
#Fun<erl_eval.6.30202343>
4> Double(4).
8
5> Temp = fun({c, C}) -> {f, 32 + C*9/5};
5>        ({f, F}) -> {c, (F-32) * 5 / 9}
5>        end.
#Fun<erl_erval.6.56003434>
6> Temp({f, 212}).
{c,100.000}

Defining Your Own Control Abstractions

for(Max, Max, F) -> [F(Max)];
for(I, Max, F) -> [F(I) | for(I + 1, Max, F)].

for(1, 10, F) creates he list [F(1),F(2), ..., F(10)].

3.6 List Comprehensions

1> L = [1,2,3,4,5].
[1,2,3,4,5]
2> lists:map(fun(X) -> 2*X end, L).
[2,4,6,8,10]
3> [2*X || X <- L].
[2,4,6,8,10]

Quicksort

qsort([]) -> [];
qsort([Pivot|T]) ->
    qsort([X || X <- T, X < Pivot])
    ++ [Pivot] ++
    qsort([X || X <- T, X >= Pivot]).

Pythagorean Triplets

pythag(N) ->
    [{A, B, C} ||
        A <- lists:seq(1, N),
        B <- lists:seq(1, N),
        C <- lists:seq(1, N),
        A + B + C =< N,
        A*A + B*B =:= C*C
    ].

Usage

1> pythag(30).
[{3,4,5},{5,12,13},{6,8,10}]

Anagrams

X--Y is the list subtraction operator. It subtracts the elements in Y from X

perms([]) -> [[]];
perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].

Usage

1> lib_misc:perms("234").
["234","243","324","342","423","432"]

List Operations ++ and --

1> [1, 2, 3] ++ [4, 5, 6].
[1,2,3,4,5,6]
2> [a,b,1,c,1,d,1] -- [1].
[a,b,c,1,d,1]
3> [a,b,1,c,1,d,1] -- [1,1].
[a,b,c,d,1]
4> [a,b,1,c,1,d,1] -- [1,1,1].
[a,b,c,d]
3> [a,b,1,c,1,d,1] -- [1,1,1,1].
[a,b,c,d]

3.7 Arithmetic Expressions

+X
-X
X*Y
X/Y (floating-point division)
bnot X (Bitwise not of X)
X div Y (Integer division of X and Y)
X rem Y (Integer remainder of X divided by Y)
X band Y (Bitwise and of X and Y)
X+Y
X-Y
X bor Y (Bitwise or of X and Y)
X bxor Y (Bitwise xor of X and Y)
X bsl N (Arithmetic bitshift left of X by N bits)
X bsr N (Bitshift right of X by N bits)

3.8 Guards

max(X, Y) when X > Y -> X;
max(X, Y) -> Y.

Examples

f(X, Y) when is_integer(X), X > Y, Y < 6 -> ...

is_tuple(T), size(T) =:= 6, abs(element(3, %)) > 5

element(4, X) =:= hd(L)

A >= -1.0 andalso A+1 > B

is_atom(L) orelse (is_list(L) andalso length(L) > 2)

The comma, which separates the test in the guard, means "and".

Guard predicates

is_atom(X)
is_binary(X)
is_constant(X)
is_float(X)
is_function(X)
is_function(X, N)
is_integer(X)
is_list(X)
is_number(X)
is_pid(X)
is_port(X)
is_reference(X)
is_tuple(X)
is_record(X,Tag)
is_record(X,Tag,N)

The True Guard

if
  Guard -> Expressions;
  Guard -> Expressions;
  ...
  true -> Expressions
end

Guard built-in functions

abs(X)
Absolute value of X.

element(N,X)
Element N of X. Note X must be a tuple.

float(X)
Convert X, which must be a number, to a float.

hd(X)
The head of the list X.

length(X)
The length of the list X.

node()
The current node.

node(X)
The node on which X was created.  X can be a process.
An identifier, a reference, or a port.

round(X)
Converts X, which must be a number, to an integer.

self()
The process identifier of the current process.

size(X)
The size of X. X can be a tuple or a binary.

trunc(X)
Truncates X, which must be a number, to an integer.

tl(X)
The tail of the list X.

3.9 Records

Records provide a method for associating a name with a particular element in a tuple.

in records.hrl

-record(todo, {status=reminder, who=joe, text})

Usage

1> rr("records.hrl").
[todo]
2> X=#todo{}.
#todo{status = reminder,who = joe,text = undefined} 
3> X1 = #todo{status=urgent, text="Fix errata in book"}.
#todo{status = urgent,who = joe,text = "Fix errata in book"} 
4> X2 = X1#todo{status=done}.
#todo{status = done,who = joe,text = "Fix errata in book"}
5> X2#todo.text.
"Fix errata in book"

3.10 case and if Expressions

case Expression of
    Pattern1 [when Guard1] -> Expr_seq1;
    Pattern2 [when Guard2] -> Expr_seq2;
    ...
end

Function filter(P, L) returns a list of all those elements X in L for which P(X) is true.

filter(P, [H|T]) -> filter1(P(H), H, P, T);
filter(P, []) -> [].

filter1(true, H, P, T) -> [H|filter(P, T)];
filter1(false, H, P, T) -> filter(P, T).

Using case expression:

filter(P, [H|T]) ->
    case P(H) of
        true -> [H|filter(P, T)];
        flase -> filter(P, T)
    end;
filter(P, []) ->
    [].

if Expressions

if
    Guard1 ->
        Expr_seq1;
    Guard2 ->
        Expr_seq2;
    ...
    true ->
        Expr_final;
end

At least one of the guards in the if expression must evaluate to true; otherwise, an exception will be raised.