SE 765 - Distributed Software Development CS 610 - Introduction to Parallel
and Distributed Computing |
Erlang 1 – Basic Programming |
This lecture uses
materials and has been adapted from:
http://www.ibm.com/developerworks/opensource/library/os-erlang1/
http://www.erlang.org/doc/pdf/otp-system-documentation.pdf
•
Erlang was developed by Ericsson to aid in the development
of software for managing a number of different telecom projects, with the first
version being released in 1986, and the first open source release of the
language in 1998.
•
Erlang includes a very simple concurrency model, allowing
individual blocks of code to be executed multiple times on the same host with
relative ease.
•
Erlang uses an error model that allows failures within
these processes to be identified and handled, even by a new process, which
makes building highly fault tolerant applications very easy.
•
Erlang includes built-in distributed processing, allowing
components to be run on one machine while being requested from another.
•
Erlang provides a great environment to build the type of
distributed, scalable, and high-performance discrete applications that we often
use to support modern network and web-based applications.
•
The main difference
between Erlang and more popular languages is that Erlang is primarily a functional programming language.
•
With functional
programming, the functions and operations of the language are designed in a
similar way to mathematical calculations, in that the language operates on
functions taking input and generating a result.
•
The functional
programming paradigm means that the individual blocks of code can produce
consistent output values for the same input values.
o This makes predicting the output of the function or
program much easier and, therefore easier to debug and analyze.
•
The contrasting
programming paradigm is the imperative programming language, such as Perl, or
Java, which relies on changing the state of the application during execution.
o The change of state in imperative programming
languages means that the individual components of a program can produce
different results with the same input values, based on the state of the program
at the time.
http://www.erlang.org/download.html
•
Most operating
systems have a command interpreter or shell, Unix and
Linux have many, Windows has the Command Prompt.
•
Erlang has its own shell where you can directly write bits
of Erlang code and evaluate (run) them to see what happens.
•
Start the Erlang shell (in Linux or UNIX) by starting a shell or
command interpreter in your operating system and typing erl, you will see something like this.
Using the Erlang shell
$ erl Erlang R13B04 (erts-5.7.5)
[source] [rq:1] [async-threads:0] Eshell V5.7.5 (abort with ^G) 1> |
•
In Windows, the shell is started by
double-clicking on the Erlang shell icon.
•
You can use the
prompt to enter statements, which you should terminate with a period.
•
The shell evaluates
the statement when completed.
•
Thus, entering a
simple sum returns the result.
1> 3+4. 7 |
Now let's try a more complex calculation.
2> (42 + 77) * 66
/ 3. 2618.0 |
•
Here
you can see the use of brackets and the multiplication operator "*"
and division operator "/", just as in normal arithmetic (see the
chapter "Arithmetic Expressions" in the Erlang
Reference Manual).
•
To
shutdown the Erlang system
and the Erlang shell type Control-C. You will see the
following output:
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution a % |
•
Type
"a" to leave the Erlang system.
Another way to shutdown the Erlang system is by entering halt():
3> halt(). % |
•
A programming language isn't much
use if you can just run code from the shell. So here is a small Erlang program.
•
Enter it into a file called tut.erl (the file name tut.erl is important, also make sure that
it is in the same directory as the one where you started erl) using a suitable text editor.
Here's the code to enter for a program that
doubles the value of numbers:
-module(tut). -export([double/1]). double(X) -> 2
* X. |
•
Erlang
programs are written in files.
•
Each
file contains what we call an Erlang module.
•
The
first line of code in the module tells us the name of the module - tut.
o Note
the "." at the end of the line.
•
The files which are used to store
the module must have the same name as the module but with the extension ".erl".
o Here
the file name is tut.erl.
•
The
second line: -export([double/1]) says
that the module tut contains
a function called double
which takes one argument (X in example) and
that this function can be called from outside the module tut.
If
you want to change directory at run time in the emulator, then use the built in
function cd as below to change to your own directory path
50> cd("c:/MyPath/erl"). c:/MyPath/erl ok |
Compile
the program in the Erlang shell:
3> c(tut). {ok,tut} |
The
{ok,tut} tells you that
the compilation was OK.
Run
the program.
4> tut:double(10). 20 |
As
expected double of 10 is 20.
•
When the function is used in
another module, we use the syntax, module_name:function_name(arguments).
•
So
tut:double(10) means call
function double
in module tut with argument
"10".
A
function can have many arguments.
-module(tut1). -export([fac/1, mult/2]). fac(1) -> 1; fac(N) -> N * fac(N -
1). mult(X, Y) -> X * Y. |
Note
that the -export
line has been expanded as well with the
information that there is another function mult with two
arguments.
Running
it gives:
8> tut1:mult(3,4). 12 |
There are two types of numeric literals, integers
and floats. Besides the conventional notation, there are two Erlangs pecific notations:
•
$char
o ASCII
value of the character char.
•
base#value
o Integer
with the base base, which must be an integer in the
range 2..36.
o In
Erlang 5.2/OTP R9B and earlier versions, the allowed
range is 2..16.
Examples:
1> 42. 42 2> $A. 65 3> $\n. 10 4> 2#101. 5 5> 16#1f. 31 6> 2.3. 2.3 7> 2.3e3. 2.3e3 8> 2.3e-3. 0.0023 |
•
Integers, and most
integer operations, are the same as in other languages. You can add two numbers
together.
1> 3+4. 7 |
And you can use
parentheses to group calculations together.
Using parentheses to group calculations
2> (4+5)*9 2>
. 81 |
Note that in the
above, the
terminating statement period is on a separate line and evaluates the preceding
calculation.
Floats in Erlang are used to represent real numbers, and can be
expressed naturally.
3> 4.5 + 6.2 . 10.7 |
Floats can also be
expressed using exponents.
Floats expressed using exponents
4> 10.9E-2 +4.5 4>
. 4.609 |
The standard
mathematical operators, +, -, /, * are supported on both integer and floating point values,
and you can mix and match floating-point and integers in calculations.
However, an error
will be raised when using an equivalent of the modulus and remainder operators
on floating values, as these support only integer values.
An atom is a literal, a constant
with name.
Examples:
8> abc. abc 9> 'Quoted
literal'. 'Quoted literal' |
·
Atoms should be used as a clearer method of specifying or
identifying a value.
·
As such, the only valid operation on an atom is a
comparison.
·
Using atoms in this way also extends to boolean logic, with the
availability of true and false atoms to identify the boolean
result of a statement.
·
Atoms must start with a lower case character, or you can
delimit with single quotes.
For example, you can
compare integers and get a boolean
atom as the result.
Comparing integers to get a boolean atom
10> 1 == 1. true |
Or you can compare
atoms, as shown below.
11> abc == def. false |
Atoms are themselves
ordered lexically (that is, z has a greater value
than a), for example.
13> a < z. true |
Standard boolean operators are available,
such as and, or, xor and not. You can also use
the is_boolean() function to check whether the supplied
value is true or false.
Example: tut2.erl) for converting
from inches to centimeters and vice versa:
-module(tut2). -export([convert/2]). convert(M,
inch) -> M /
2.54; convert(N,
centimeter) -> N *
2.54. |
Compilation and running:
9> c(tut2). {ok,tut2} 10> tut2:convert(3,
inch). 1.1811023622047243 11> tut2:convert(7,
centimeter). 17.78 |
•
Tuples are a
composite data type and are used to store collections of items.
•
Tuples are delimited
by curly brackets.
•
Each term Term
in
the tuple is called an element.
•
The number of elements is said to be the
size of the tuple.
14> {abc, def, {0, 1}, ghi}. {abc,def,{0,1},ghi} |
·
The contents of a
tuple do not all have to be the same type, but one special construct is of a
tuple where the first value is an atom.
·
In this case the
first atom is called a tag and this can be used to identify or classify the
contents.
Tuple where the first value is an atom
16> { email, 'example@example.org'}. {email,'example@example.org'} |
Here the tag is
email, and the tag can be used to help identify the remainder of the content in
the tuple.
Tuples are very
useful for containing defined elements and describing different complex data
structures, and Erlang
allows you to set and get values in a tuple explicitly).
Setting and getting values in a tuple explicitly
17> element(3,{abc,def,ghi,jkl}). ghi 18> setelement(3,{abc,def,ghi,jkl},mno). {abc,def,mno,jkl} |
Note that the
elements of the tuple are indexed with 1 as the first value, instead of 0, which is common in most other languages.
You can also compare
tuples in their entirety.
Comparing tuples in their entirety
19> {abc,def} == {abc,def}. true 20> {abc,def} == {abc,mno}. false |
Example: Rewriting metric conversion
-module(tut3). -export([convert_length/1]). convert_length({centimeter, X}) -> {inch, X / 2.54}; convert_length({inch, Y}) -> {centimeter, Y *
2.54}. |
Compile
and run:
14> c(tut3). {ok,tut3} 15> tut3:convert_length({inch,
5}). {centimeter,12.7} 16> tut3:convert_length(tut3:convert_length({inch,
5})). {inch,5.0} |
•
Note
on line 16 we convert 5 inches to centimeters and back again and reassuringly
get back to the original value.
o
i.e the argument to
a function can be the result of another function.
•
Line
16 –
o
The
argument given the function {inch,5} is first matched against the
first head clause of convert_length
§
i.e.
convert_length({centimeter,X}) where it can be
seen that {centimeter,X} does not match {inch,5}
(the
head is the bit before the "->").
o This having
failed, we try the head of the next clause i.e. convert_length({inch,Y}), this matches and Y get the value 5.
The last data type
is the list, which is denoted by square brackets.
Lists and tuples are
similar, but whereas a tuple can only be
used in a comparison, lists allow a
wider variety of manipulation operations to be performed.
A list is a compound data type
with a variable number of terms.
[Term1,...,TermN] |
•
Each
term Term in the list is
called an element.
•
The
number of elements is said to be the length of the list.
•
Formally,
a list is either the empty list [] or consists of a
head (first element) and a tail (remainder of the list) which is also
a list.
•
The
latter can be expressed as [H|T]. The notation [Term1,...,TermN] above is
actually shorthand for the list [Term1|[...|[TermN|[]]]].
Example:
[] is
a list, thus
[c|[]]
is a list, thus
[b|[c|[]]]
is a list, thus[a|[b|[c|[]]]]
is a list, or in short [a,b,c].
1> L1 = [a,2,{c,4}]. [a,2,{c,4}] 2> [H|T] = L1. [a,2,{c,4}] 3> H. a 4> T. [2,{c,4}] 5> L2 = [d|T]. [d,2,{c,4}] 6> length(L1). 3 7> length([]). 0 |
Strings are a
special type of list.
·
Erlang does not support the idea of a string directly, although
you can use a double quoted value to create a string value.
Using a double quoted value to create a string value
23>
"Hello". "Hello" |
·
However, a string is
actually just a list of ASCII integer values.
·
Thus the above
string is stored as a list of the ASCII character values.
String stored as a list of the ASCII character values
24>
[72,101,108,108,111]. "Hello" |
As a shortcut, you
can also specify characters using the $Character notation
Specifying characters using the $Character notation
25> [$H,$e,$l,$l,$o]. "Hello" |
Lists,
including strings (lists of characters), support a number of different methods
for manipulation.
This highlights the
main difference between a string and an atom:
·
Atoms
are static identifiers, but you can manipulate a string by examining the
component parts (each character).
·
You
cannot, for example, identify the individual words in the atom (for example, 'Quick brown fox')
because the atom is a single entity.
·
But
a string could be split into different words: ["Quick","brown","fox"].
·
A
number of functions for manipulating lists are provided in the lists module
·
For
example, you can sort the items in a list using the sort function. As these are
note built-in functions, you have to specify the module and function name.
Specifying the module and function name
34> lists:sort([4,5,3,2,6,1]). [1,2,3,4,5,6] |
·
You
can construct lists with multiple elements using the constructor, which builds
a list from an element and another list.
·
In
other languages, this kind of construction is handled by functions or operators
for push().
·
In
Erlang, the | (pipe) operator is used to differentiate between the
head (start of the list) and the tail, in the notation [Head|Tail].
·
Here
the head is a single element, and the tail is the rest of the list
·
The
code below shows how to add a new value to the beginning of the list.
Adding a new value to beginning of the list
29> [1|[2,3]]. [1,2,3] |
You can repeat this
for the entire list.
Repeating this for the entire list
31> [1|[2|[3|[]]]]. [1,2,3] |
In this example, the
empty list at the end means that you have constructed a well-formed (proper)
list.
Note that the first
item must an element, not a list fragment. If you merge the other way, you
construct a nested list.
30> [[1,2]|[2,3]]. [[1,2],2,3] |
Finally, you can
merge lists using the ++ operator.
Using the ++ operator to merge lists
35> [1,2] ++ [3,4]. [1,2,3,4] |
And, you can
subtract each element from the list on the right of the operator from the list
on the left.
Subtracting each element from the list
36> [1,2,3,4] -- [2,4]. [1,3] |
Because strings are
lists, the same is true for them too.
37>
"hello" ++ "world". "helloworld" 40>
("hello" -- "ello") ++ "i". "hi" |
One important
element of expressions is the variable.
Variables in Erlang must start with a capital letter and be followed by
any combination of upper and lowercase letters and underscores.
Variables in Erlang
41> Value = 99. 99 |
·
Assignment
of a variable within Erlang is a one-time binding of
the value to the variable.
·
Once
you have bound the variable once, you cannot change it's value.
42> Value = 100. ** exception error:
no match of right hand side value 100 |
·
This
is unlike most languages — the very definition of variable tends to imply that
the value is variable.
·
Single
assignment in this means that if you want to calculate the result of a value
you must assign it to a new variable.
Limitations of variables in Erlang
43> Sum = Value +
100 199 |
·
The
benefit of this single assignment is that it makes it more difficult to
introduce or accidentally change the value of a variable during a calculation, which
makes identifying a value and debugging during programming much easier.
·
It
also makes the code clearer, and sometimes much shorter, as you can simplify
the structure.
·
Note that this
operation means that the value is calculated before being bound to the
variable.
·
You can explicitly
forget the bounding of a variable using f(Varname), or all variables using f().
Assigning values to
variables is actually a special type of pattern matching.
·
Pattern
matching in Erlang also handles the execution flow of
individual statements, and extracts the individual vales from compound data
types (tuples, arrays).
·
The
basics of pattern matching is therefore: Pattern =
Expression.
o
The expression is
composed of data structures, bound variables (that is, those with a value),
mathematical operations and function calls.
o
The two sides of the
operation must match (that is, if you have 2 element tuple as the pattern, the
expression must resolve to a 2 element tuple).
o
When the expression
is executed, the expression is evaluated, and the results assigned to the
pattern.
o
For example, we can
assign values to two variables simultaneously with one pattern matching.
Assigning values to two variables simultaneously
48> {A,B} = {(9+45),abc}. {54,abc} |
Note
:
·
if the pattern is a bound variable, or the element of the
pattern is a bound variable, then the result of the pattern match becomes a
comparison.
·
This allows for more
powerful selective assignment.
o
For example, to get
the name and email from a tuple, we can use pattern matching: {contact, Name, Email} = {contact,
"Me", "me@example.com"}.
·
Finally, we can use
pattern matching to pull out the elements from a list or tuple using the
construct notation mentioned earlier.
o
For example, the following code shows how
to get the first two elements from a list while retaining the remainder.
Getting the first two elements from a list while
retaining the remainder
53> [A,B|C] = [1,2,3,4,5,6]. [1,2,3,4,5,6] |
·
A has been assigned the value 1,
·
B the value 2,
·
C the remainder of the list.
A simple
way to use to use the io:format function.
31> io:format("hello world~n",
[]). hello world ok 32> io:format("this outputs one Erlang term: ~w~n",
[hello]). this outputs one Erlang
term: hello ok 33> io:format("this outputs two Erlang terms: ~w~w~n",
[hello, world]). this outputs two Erlang
terms: helloworld ok 34> io:format("this outputs two Erlang terms: ~w ~w~n",
[hello, world]). this outputs two Erlang
terms: hello world ok |
•
The function format/2 (i.e. format with two
arguments) takes two lists.
•
The first one is nearly always a
list written between " ".
•
This list is printed out as it
stands, except that each ~w is
replaced by a term taken in order from the second list.
•
Each ~n is replaced by a new line.
•
The io:format/2 function
itself returns the atom ok if
everything goes as planned.