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

http://www.claystuart.com/

 

What is Erlang?

         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.

Functional programming versus other paradigms

         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.

Where to get Erlang

http://www.erlang.org/download.html

The Erlang Shell

         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.

Entering a simple sum

          

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().

%


Modules and Functions

         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

 

 

Data Types

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.

Adding 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.

Floats 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.


Atoms

An atom is a literal, a constant with name.

Examples:

Atoms

 

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.

Comparing atoms

 

11> abc == def.

false

 

Atoms are themselves ordered lexically (that is, z has a greater value than a), for example.

Atoms ordered lexically

 

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

         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.

Tuples

 

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.

 


Lists

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

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]

 


List manipulation

·         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.

Constructing 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.

The same is true for strings

 

37> "hello" ++ "world".

"helloworld"

40> ("hello" -- "ello") ++ "i".

"hi"

 

 


Expressions and pattern matching

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.

Binding a value to a variable

 

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.


Writing Output to a Terminal

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.