Getting started with Erlang

Erlang has long been criticised for its unintuitive syntax which introduces a lot of threshold for a new learner. I won't argue or challenge this widespread belief but through this article I will try to help people learning Erlang to cross this learning threshold.

Before proceeding further please make sure you have Erlang installed on your system. Once you have installed it open up the Erlang shell because this tutorial will be hands on !


Before we begin one thing I want you to engrave onto your brain is the dot i.e. “.”

Eshell V7.0  (abort with ^G)
1> ok.
ok
2>

Notice the “.” at the end of “ok”, that will be required whenever you complete a statement. Recall from your english class whenever you complete a sentence you end it with a dot ! Erlang can also be seen as a set of sentences each of which ends with a “.” It will become more and more clear when and where to put “.” by the end of this tutorial. But as of now just remember the sentence analogy I gave you.

Erlang is nothing but a set of sentences.

Variables Types

This is the first thing we will deal with. Following are a few variable types that you will need as of now(in erlang comments begin with %),

Eshell V7.0  (abort with ^G)
1> "Hello World". % this is how you write a string
"Hello World"
2> 42. % this is how you write a number
42
3> 4.2. % this how to write a decimal
4.2
4> ["Hello", 4.2]. % this how to write a list
["hello",4.2]
5> {"Hello", 4.2}. % this how to write a tuple

There are a lot of other exotic variable types in Erlang but since we are beginning with Erlang we will start with some common ground.

Variables

Now that you have knowledge about variables types you would want to store them somewhere and do stuff to them ! Following is how you declare a variable

6> SomeVar = "Hello World".
"Hello World"

NOTE:

  • variable name in Erlang ALWAYS starts with a capital letter ! (don’t fight this … just accept it and move ahead)

Another thing about variables is that …. they are immutable ! …. there I said it. You can't change the value of a variable once you assign it to something. Don't freak out you will survive it and then you will embrace it !

7> NewVar = "Hello World".
"Hello World"
8> NewVar = "Testing".
** exception error: no match of right hand side value "Testing"

If you see above, it gave an error when we try to reassign the variable NewVar to the value “Testing”. Another thing to notice is the error, it says that “no match of right hand side value “Testing” ”, meaning that it was trying to compare them ! But you didn't ask it compare them. This is what Erlang does, for a variable which has been assigned a value it will try to compare or match it if you try to reassign it, and for a variable which is not assigned or unbound it will assign it the value. Have a look below

9> Var1.
* 1: variable 'Var1' is unbound
10> Var1 = "Hello World".
"Hello World"
11> Var1 = "New stuff".
** exception error: no match of right hand side value "New stuff"
12> Var1 = "Hello World".
"Hello World"
  • At 9 we didn’t have the variable assigned so it said unbound, that is what it always says if the variable has not been assigned any value.
  • At 10 we assigned a value and everything is fine.
  • At 11 we try to reassign and it throws an error.
  • At 12 we assigned “Hello World” value again and it's happy. What ! I thought that variables could not be reassigned ?? The reason the last statement worked because the value stored in Var1 matched to “Hello World” so no reassignment has taken place but the match is successful !

Atoms

Here I will introduce another variable type called “atom”. I know I said we will start with some common ground but atoms are really at heart of what we are going to do next. Following is how to write an atom.

13> helloworld.
helloworld

Notice that it begins with a small letter, this is the reason why in Erlang variables begin with capital letters because if it begins with a small letter then it’s an atom. So what to do when you want to write an atom with a capital letter, you will never need it but since this question is going to pop up in your head this is how to do it,

14> 'Hello World'.
'Hello World'

Notice the single quote, strings are written with double quotes and atom with single quotes.

So you might be wondering what is this variable type atom ?? Remember atoms from your physics class, yeah ! the variable type atom has nothing to do with that ! This variable type atom has no moving parts, i.e. the helloworld is just helloworld you can't manipulate it. Basically you can't do anything with it !

So you might be wondering what can one do with atoms ! To that, I would say hold on and thou shall see.

Module

Now that we are through with writing variables, let’s learn how to create a module and write your first Erlang program. Create a file named “hello_world.erl” and add the following to it,

-module(hello_world).
-export([hello_world/0]).
hello_world() -> "Hello World".
  • The first line declares the module name and the module name should be same as that of the filename. Since our file was named “hello_world.erl” we used “-module(hello_world).” . Notice that module name is atom (first usage of atoms !)
  • The second line allows user to define a list of function names that you want to expose to the universe. Notice the list element hello_world/0 , here the last part i.e. “/0” represent the arity (the number of argument parameters the function takes as input) of the function. An example usage of export is follows (notice the function names are also atoms !),
-export([first_function/1, second_function/2]).
  • Here we expose 2 functions i.e. first_function and second_function. The first_function has an arity of 1 and second_function has an arity of 2.
  • In the third line of our module we write our first function i.e.
hello_world() -> "Hello World".
% function_name(Arg1, Arg2, ...) -> do_something.
  • notice the odd “->” , you have to use it after you complete writing the function head. Just like in python you write,
def fuction_name(args):
return "ok"

In Erlang we write,

funtion_name(args) ->
ok.
% or
funtion_name(args) -> ok.
  • The last statement in an Erlang functions is automatically returned. The above function returns the atom “ok”.
  • Another thing to notice is how we have used “.” Recall that I said whenever we complete a sentence you have to use “.” So I wrote the above module i.e. hello_world.erl as set of sentences such that each one has a “.” at the end of it. To make it explicit whenever you end a function use “.”
Functions are like sentences so they end with “.”

Now we write a few more functions in our hello_world.erl module:

add(A, B) -> A + B.
multiply(A, B) -> A*B.
divide(A, B) -> A/B.

So now our complete module looks like as follows

-module(hello_world).
-export([hello_world/0, add/2, multiply/2, divide/2]).
hello_world() -> "Hello World".
add(A, B) -> A + B.
multiply(A, B) -> A*B.
divide(A, B) -> A/B.

Let’s compile and run our first module,

15> c(hello_world).
ok

to compile your module use the above in-built function c(module_name). Following is how you can access your functions from the module (we use module_name:funtion_name()),

16> hello_world:hello_world().
"Hello World"
17> hello_world:add(1,2).
3
18> hello_world:multiply(1,2).
2
19> hello_world:divide(1,2).
0.5

Till now we have written some basic functions, now we will write something more complicated,

area_triangle(A, B, C) ->
S = (A + B + C)/2,
math:sqrt(S*(S-A)*(S-B)*(S-C)).
  • The first line is the function head, function name is area_triangle which takes the 3 variables i.e. the sides of the triangle as argument.
  • The second line does some calculation but the thing to notice here is the “,” . So when you write a multi line function we need to tell the compiler where a segment ends. Also when you write the last segment for your function you end it with “.” because this completes your and recall that when we complete a sentence we end it with “.” So you can view the above function like a sentence as below where each of the segment end with a “,” and the last segment end with “.” because it marks the end of sentence.
area_triangle(A, B, C) -> S = (A + B + C)/2, math:sqrt(S*(S-A)*(S-B)*(S-C)).
  • In the last line we just use the sqrt function from the in-built math module to calculate the square root.
The lesson learnt from above exercise is that we use “,” when we end a segment and “.” when we end a statement.

Pattern Matching

The simplest way to put pattern matching is that things that look similar will match and things that don't will not … huhh obvious, right ! for eg.

I love erlang.

I love erlang.

The above two sentence are same so they will pattern match !

Var1 = "I love Erlang".
Var2 = "I love Erlang".
Var1 = Var2. % output will be: "I love Erlang"

Recall that we used a get a match error when we re-assigned a variable. Have a look below

1> Var1 = "I love Erlang".
"I love Erlang"
2> Var1 = "something else".
** exception error: no match of right hand side value "something else"

You see this is match error ! It was trying to pattern match it, now you understand why it fails because the only thing that will match is “I love Erlang”.

Now we see a little more evolved example of pattern match,

1> List = ["I", "love", "Erlang"].
["I","love","Erlang"]
2> [A, B, C] = List.
["I","love","Erlang"]
3> A.
"I"
4> B.
"love"
5> C.
"Erlang"
6> [A, B, C] = ["I", "hate", "Erlang"].
** exception error: no match of right hand side value ["I","hate","Erlang"]
  • In the first line we just assign a list to the variable “List”.
  • Next we try to pattern match “List” with [A, B, C] which is a list of unbound variables. Recall, I told you that Erlang will assign values to variables if they unbound and match them if they are bound. Since all “A”, “B” and “C” are unbound and the list sizes are same on both sides i.e. 3 the match ends up assigning values to “A”, “B” and “C”.
  • In the sixth line we try to match again but since variables already have value assigned the match fails. In short you can't hate Erlang !

If you reassess the above we can extract values using pattern match. But what if you don't want to get all the values from the list ?

1> List = ["I", "love", "Erlang", "even", "more"].
["I","love","Erlang","even","more"]
2> [A, B | C] = List.
["I","love","Erlang","even","more"]
3> A.
"I"
4> B.
"love"
5> C.
["Erlang","even","more"]
6>

Here I have extracted only the first two values and the rest of the list is available in “C”. You can extract any amount of values and leave the rest for example:

1> List = ["I", "love", "Erlang"].
["I","love","Erlang"]
2> [A, B, C | D] = List.
["I","love","Erlang"]
3> D.
[]
4> [E, F, G, H | I] = List.
** exception error: no match of right hand side value ["I","love","Erlang"]

Notice here “D” is just an empty list. Another thing to notice here is line 4, you get an error here because there are only 3 values in the list after which you will get an empty list in the fourth variable (as discussed) after which there is nothing to match so it gives an error.

You can apply pattern matching to other things like tuples and list of tuples or anything reasonable you can imagine.

Functions and Pattern matching

You can pattern match in functions too ! But how is that helpful you might ask. Ok let see some examples,

is_zero(0) -> true;
is_zero(N) -> false.

there are quite a few things that happened here let’s see them one by one (here “true” and “false” are atoms, so yet another usage of atoms !)

  • Firstly, these are not two functions it is a single function which two clauses. The first clause ends at “;” after which the second clause begins. In general clauses in Erlang end with “;” . I will give a few other places where clauses come into picture later.
  • How does pattern match work here ? Suppose you use the above function as follows,
is_zero(7).   % false

When you call this function Erlang tries to execute the first clause but as you can see the only way the first clause gets executed is when the argument is zero i.e. 0 . Since the pattern match for the first clause fails it will try to execute the second clause which will match because “N” is unbound. So argument value is now stored in “N” and the function executes and returns the last statement which is atom “false”.

All in all pattern matching works on function arguments which are nothing but variables. So all the pattern matching examples discussed previously apply here. Let’s discuss some examples,

array_sum([], Sum) ->
Sum;
array_sum([Element | Rest ], Sum) ->
array_sum(Rest, Sum + Element).

We use it as follows,

array_sum([1,2,3,4,5], 0).  % 15

Let’s dissect how this function works,

  • When you execute this function, recall it will try to pattern match the first clause but it will fail because the first argument in the function is an empty list i.e. “[]” but we passed in a list which is not empty so it will fail.
  • Then it tries to match the second clause which will match because all the arguments are unbound and they get bound to the values we pass in. But for the first argument the pattern match will also extract an array element and bind it to “Element” and rest of the array will be stored in “Rest”.
  • We then call array_sum again but this time we pass “Rest” instead of the whole array and add the variable “Element” extracted from the list to “Sum”. So our next call will look like this
array_sum([2,3,4,5], 1)
array_sum([3,4,5], 3)   % next call
  • This way each element will get added to “Sum” and finally we will have “[]” i.e. empty list. Now the empty list match the first clause and at this point we know we have no elements left to add. So we returns the variable “Sum”.

I think with this you will be able to get off your feet and be able to dive deeper into Erlang.

Lastly I will leave you with some examples of clauses you will find while you are learning Erlang (I promised to do so right !). I will basically rewrite the is_zero/1 function using these different types of clauses:

%% case statements
is_zero(N) ->
case SomeVariable of
0 -> true;
N -> false
end.
%% if statement
is_zero(N) ->
if
N =/= 0 -> false;
true -> true
end.

So the case statements has clauses and if statements have clauses.


Where to go from here ?

If you found it helpful then please share it !