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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
|
---
language: erlang
contributors:
- ["Giovanni Cappellotto", "http://www.focustheweb.com/"]
filename: learnerlang.erl
---
```erlang
% Percent sign starts a one-line comment.
%% Two percent characters shall be used to comment functions.
%%% Three percent characters shall be used to comment modules.
% We use three types of punctuation in Erlang.
% Commas (`,`) separate arguments in function calls, data constructors, and
% patterns.
% Periods (`.`) (followed by whitespace) separate entire functions and
% expressions in the shell.
% Semicolons (`;`) separate clauses. We find clauses in several contexts:
% function definitions and in `case`, `if`, `try..catch` and `receive`
% expressions.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 1. Variables and pattern matching.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Num = 42. % All variable names must start with an uppercase letter.
% Erlang has single assignment variables, if you try to assign a different value
% to the variable `Num`, you’ll get an error.
Num = 43. % ** exception error: no match of right hand side value 43
% In most languages, `=` denotes an assignment statement. In Erlang, however,
% `=` denotes a pattern matching operation. `Lhs = Rhs` really means this:
% evaluate the right side (Rhs), and then match the result against the pattern
% on the left side (Lhs).
Num = 7 * 6.
% Floating point number.
Pi = 3.14159.
% Atoms, are used to represent different non-numerical constant values. Atoms
% start with lowercase letters, followed by a sequence of alphanumeric
% characters or the underscore (`_`) or at (`@`) sign.
Hello = hello.
OtherNode = example@node.
% Atoms with non alphanumeric values can be written by enclosing the atoms
% with apostrophes.
AtomWithSpace = 'some atom with space'.
% Tuples are similar to structs in C.
Point = {point, 10, 45}.
% If we want to extract some values from a tuple, we use the pattern matching
% operator `=`.
{point, X, Y} = Point. % X = 10, Y = 45
% We can use `_` as a placeholder for variables that we’re not interested in.
% The symbol `_` is called an anonymous variable. Unlike regular variables,
% several occurrences of _ in the same pattern don’t have to bind to the same
% value.
Person = {person, {name, {first, joe}, {last, armstrong}}, {footsize, 42}}.
{_, {_, {_, Who}, _}, _} = Person. % Who = joe
% We create a list by enclosing the list elements in square brackets and
% separating them with commas.
% The individual elements of a list can be of any type.
% The first element of a list is the head of the list. If you imagine removing the
% head from the list, what’s left is called the tail of the list.
ThingsToBuy = [{apples, 10}, {pears, 6}, {milk, 3}].
% If `T` is a list, then `[H|T]` is also a list, with head `H` and tail `T`.
% The vertical bar (`|`) separates the head of a list from its tail.
% `[]` is the empty list.
% We can extract elements from a list with a pattern matching operation. If we
% have a nonempty list `L`, then the expression `[X|Y] = L`, where `X` and `Y`
% are unbound variables, will extract the head of the list into `X` and the tail
% of the list into `Y`.
[FirstThing|OtherThingsToBuy] = ThingsToBuy.
% FirstThing = {apples, 10}
% OtherThingsToBuy = {pears, 6}, {milk, 3}
% There are no strings in Erlang. Strings are really just lists of integers.
% Strings are enclosed in double quotation marks (`"`).
Name = "Hello".
[72, 101, 108, 108, 111] = "Hello".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 2. Sequential programming.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Modules are the basic unit of code in Erlang. All the functions we write are
% stored in modules. Modules are stored in files with `.erl` extensions.
% Modules must be compiled before the code can be run. A compiled module has the
% extension `.beam`.
-module(geometry).
-export([area/1]). % the list of functions exported from the module.
% The function `area` consists of two clauses. The clauses are separated by a
% semicolon, and the final clause is terminated by dot-whitespace.
% Each clause has a head and a body; the head consists of a function name
% followed by a pattern (in parentheses), and the body consists of a sequence of
% expressions, which are evaluated if the pattern in the head is successfully
% matched against the calling arguments. The patterns are matched in the order
% they appear in the function definition.
area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R}) -> 3.14159 * R * R.
% Compile the code in the file geometry.erl.
c(geometry). % {ok,geometry}
% We need to include the module name together with the function name in order to
% identify exactly which function we want to call.
geometry:area({rectangle, 10, 5}). % 50
geometry:area({circle, 1.4}). % 6.15752
% In Erlang, two functions with the same name and different arity (number of arguments)
% in the same module represent entirely different functions.
-module(lib_misc).
-export([sum/1]). % export function `sum` of arity 1 accepting one argument: list of integers.
sum(L) -> sum(L, 0).
sum([], N) -> N;
sum([H|T], N) -> sum(T, H+N).
% Funs are "anonymous" functions. They are called this way because they have no
% name. However they can be assigned to variables.
Double = fun(X) -> 2*X end. % `Double` points to an anonymous function with handle: #Fun<erl_eval.6.17052888>
Double(2). % 4
% Functions accept funs as their arguments and can return funs.
Mult = fun(Times) -> ( fun(X) -> X * Times end ) end.
Triple = Mult(3).
Triple(5). % 15
% List comprehensions are expressions that create lists without having to use
% funs, maps, or filters.
% The notation `[F(X) || X <- L]` means "the list of `F(X)` where `X` is taken
% from the list `L`."
L = [1,2,3,4,5].
[2*X || X <- L]. % [2,4,6,8,10]
% A list comprehension can have generators and filters which select subset of the generated values.
EvenNumbers = [N || N <- [1, 2, 3, 4], N rem 2 == 0]. % [2, 4]
% Guards are constructs that we can use to increase the power of pattern
% matching. Using guards, we can perform simple tests and comparisons on the
% variables in a pattern.
% You can use guards in the heads of function definitions where they are
% introduced by the `when` keyword, or you can use them at any place in the
% language where an expression is allowed.
max(X, Y) when X > Y -> X;
max(X, Y) -> Y.
% A guard is a series of guard expressions, separated by commas (`,`).
% The guard `GuardExpr1, GuardExpr2, ..., GuardExprN` is true if all the guard
% expressions `GuardExpr1, GuardExpr2, ...` evaluate to true.
is_cat(A) when is_atom(A), A =:= cat -> true;
is_cat(A) -> false.
is_dog(A) when is_atom(A), A =:= dog -> true;
is_dog(A) -> false.
% A `guard sequence` is either a single guard or a series of guards, separated
%by semicolons (`;`). The guard sequence `G1; G2; ...; Gn` is true if at least
% one of the guards `G1, G2, ...` evaluates to true.
is_pet(A) when is_dog(A); is_cat(A) -> true;
is_pet(A) -> false.
% Records provide a method for associating a name with a particular element in a
% tuple.
% Record definitions can be included in Erlang source code files or put in files
% with the extension `.hrl`, which are then included by Erlang source code
% files.
-record(todo, {
status = reminder, % Default value
who = joe,
text
}).
% We have to read the record definitions into the shell before we can define a
% record. We use the shell function `rr` (short for read records) to do this.
rr("records.hrl"). % [todo]
% Creating and updating records:
X = #todo{}.
% #todo{status = reminder, who = joe, text = undefined}
X1 = #todo{status = urgent, text = "Fix errata in book"}.
% #todo{status = urgent, who = joe, text = "Fix errata in book"}
X2 = X1#todo{status = done}.
% #todo{status = done,who = joe,text = "Fix errata in book"}
% `case` expressions.
% `filter` returns a list of all elements `X` in a list `L` for which `P(X)` is
% true.
filter(P, [H|T]) ->
case P(H) of
true -> [H|filter(P, T)];
false -> filter(P, T)
end;
filter(P, []) -> [].
filter(fun(X) -> X rem 2 == 0 end, [1, 2, 3, 4]). % [2, 4]
% `if` expressions.
max(X, Y) ->
if
X > Y -> X;
X < Y -> Y;
true -> nil;
end.
% Warning: at least one of the guards in the `if` expression must evaluate to true;
% otherwise, an exception will be raised.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 3. Exceptions.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Exceptions are raised by the system when internal errors are encountered or
% explicitly in code by calling `throw(Exception)`, `exit(Exception)` or
% `erlang:error(Exception)`.
generate_exception(1) -> a;
generate_exception(2) -> throw(a);
generate_exception(3) -> exit(a);
generate_exception(4) -> {'EXIT', a};
generate_exception(5) -> erlang:error(a).
% Erlang has two methods of catching an exception. One is to enclose the call to
% the function, which raised the exception within a `try...catch` expression.
catcher(N) ->
try generate_exception(N) of
Val -> {N, normal, Val}
catch
throw:X -> {N, caught, thrown, X};
exit:X -> {N, caught, exited, X};
error:X -> {N, caught, error, X}
end.
% The other is to enclose the call in a `catch` expression. When you catch an
% exception, it is converted into a tuple that describes the error.
catcher(N) -> catch generate_exception(N).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 4. Concurrency
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Erlang relies on the actor model for concurrency. All we need to write
% concurrent programs in erlang are three primitives: spawning processes,
% sending messages and receiving messages.
% To start a new process we use the `spawn` function, which takes a function
% as argument.
F = fun() -> 2 + 2 end. % #Fun<erl_eval.20.67289768>
spawn(F). % <0.44.0>
% `spawn` returns a pid (process identifier), you can use this pid to send
% messages to the process. To do message passing we use the `!` operator.
% For all of this to be useful we need to be able to receive messages. This is
% achieved with the `receive` mechanism:
-module(calculateGeometry).
-compile(export_all).
calculateArea() ->
receive
{rectangle, W, H} ->
W * H;
{circle, R} ->
3.14 * R * R;
_ ->
io:format("We can only calculate area of rectangles or circles.")
end.
% Compile the module and create a process that evaluates `calculateArea` in the shell
c(calculateGeometry).
CalculateArea = spawn(calculateGeometry, calculateArea, []).
CalculateArea ! {circle, 2}. % 12.56000000000000049738
% The shell is also a process, you can use `self` to get the current pid
self(). % <0.41.0>
```
## References
* ["Learn You Some Erlang for great good!"](http://learnyousomeerlang.com/)
* ["Programming Erlang: Software for a Concurrent World" by Joe Armstrong](http://pragprog.com/book/jaerlang/programming-erlang)
* [Erlang/OTP Reference Documentation](http://www.erlang.org/doc/)
* [Erlang - Programming Rules and Conventions](http://www.erlang.se/doc/programming_rules.shtml)
|