The Erlang programming language is used to build massive scalable real-time software systems that require high availability. Some of its uses are in telecom, banking, e-commerce, computer telephony and instant messaging. The runtime system of Erlang has built-in support for concurrency, distribution and fault tolerance. Read on to get more familiar with it.
The first time I programmed with a functional programming language was back in 1996 while doing my MSc at UCL (University College London). The language was called SML or Standard ML. Like all functional programming languages, a key feature of SML is the function, which is used for abstraction. Nowadays, functional programming languages like Erlang are back.
Joe Armstrong created Erlang at the Ericsson Computer Science Laboratory. At the time of writing this article, the latest Erlang version is R16B.
Installing and running Erlang
Your Linux/UNIX distribution probably includes a ready-to-install Erlang package; so go ahead and install it. After doing so, try running the erl command which should give you the following output:
$ erl Erlang R16B (erts-5.10.1)[64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V5.10.1 (abort with ^G)
1>
If the output you get is similar, then Erlang has been successfully installed; so try to type the following commands inside the Erlang shell to get to know more about it:1> pwd(). /Users/mtsouk/docs/article/Erlang.OSFY ok 2> cd(".."). /Users/mtsouk/docs/article ok 3> 5-4. 1 4> 10*v(3). 10 5> math:pi(). 3.141592653589793 6> q(). ok 7> mtsouk$Now, lets look at each executed command:
Command 1: The pwd() command prints the current working directory. You can understand that each command ends with a dot .. The output is presented after typing the . and pressing the Return key.
Command 2: The second command changes the working directory and prints the new directory.
Command 3: This subtracts two integer numbers and prints the result.
Command 4: Now a tricky thing happens. We multiply the result of the third command, represented by v(3), by 10.
Command 5: This command prints the value of number pi as stored in the math Erlang module. The result is a float number.
Command 6: The q() function is the easiest way to exit the Erlang shell. The q() function quits everything Erlang is doing. If you are working locally, then there is no problem but if you are working on a remote system, you better quit by typing Ctrl-G and then q. The reason is that you may shut down the Erlang runtime on the remote machine when you quit using q()!
You are now ready to learn more interesting things about Erlang so keep reading, but it would also be a good idea to first experiment with the Erlang shell on your own.Getting to know Erlang
Erlang was developed at Ericsson in order to serve the switching of telephone equipment. Although it looks like a special purpose programming language, it is not. Actually, if you are using a NoSQL data store such as CouchDB or Riak, you are already using Erlang without knowing it!
The first thing that you need to do is forget everything you know about classes, variables and assignment of variables. You will need to think more in terms of pattern matching, pathways and message passing. This may sound weird, but its much more fun to work in this way because of the power it gives you!
Some of the main characteristics of Erlang are:
- It makes difficult things easy.
- It makes easy things difficult.
- Erlang was not designed for big data, but was designed for massive concurrency.
- It is a great language for fault tolerance and distributed applications.
- It concentrates more on data flow.
- You should design your programs based on recursion and message passing.
- Erlang can program Web applications.
- The syntax of Erlang does not look like a C-based language.
- Variables start with capital letters in Erlang. Words starting with lower case letters are atoms.
- Atoms are literals, constants with their own name for value.
- The dbg module and the erlang:trace functions are extremely helpful tools for debugging your code.
- Erlang allows you to build applications that tolerate failure and restore their functionality.
- Erlang comes with its own DBMS called Mnesia!
- You can easily create lightweight HTTP servers using the MochiWeb module.
Please do not forget that Erlang cannot solve every programming problem equally well – you may have to use another programming language for a specific type of software. Always use the best tool for the job.
The Erlang syntax may look strange at first but if you start writing Erlang programs, you will stop thinking about its syntax in less than a week! Your first exposure to Erlang may confuse or frustrate you, while also leaving you impressed and excited!
Erlang uses two kinds of numbers, integers and floats. 1 is an integer, whereas 2.0 is a floating point number. Integer numbers are always precise but floating-point numbers may get truncated a bit. When you are using floats, you should always have a number to the left of the decimal point. In other words, you cannot write .6 but you should write 0.6.
When declaring a variable inside the Erlang shell, you cannot change its value, as the following example shows:
1> MyVar = 12. 12 2> MyVar = MyVar + 5. ** exception error: no match of right hand side value 17 3> MyVar = 5. ** exception error: no match of right hand side value 5
Erlang interprets MyVar = MyVar + 5 as 12 = 12 + 5 which is obviously wrong. Erlang uses the single assignment model, which allows each variable to be assigned a value only once in a given session or context.
When you are using the Erlang shell (the erl command), every command is interpreted before being executed. When you want Erlang to compile a file, it converts the code into a BEAM file. A key part of the Erlang Runtime System (ERTS) is the BEAM processor. BEAM stands for Bodgan’s Erlang Abstract Machine, which is a virtual machine that interprets optimised BEAM code.
The built-in line editor of erl is a subset of Emacs.
The Erlang shell is the perfect place to test most of your stuff before creating a module.
What is the Erlang OTP framework?
OTP stands for Open Telecom Platform and forms Erlangs open source libraries and tools. Despite its name, OTP is about much more than just telephones.
It is the most important and vital part of Erlang. If one third of Erlang’s greatness comes from its concurrency and distribution, and another third comes from its error handling capabilities, then the OTP framework comprises the last third.
OTP was designed for building projects with big teams. It has been rewritten twice before, so it is now in its third version. It is about taking all the generic components, putting them into libraries, making sure they work well and reliably, and then reusing that code when possible. So, what is left to be done? The programmer has to only deal with things that will always change from application to application.
If you are going to write real world Erlang software, you will eventually have to learn and master the OTP framework.
A more advanced Erlang example
When creating programs, Erlang programmers put their code in modules, which help you organise, store and share code easily and efficiently and is the preferred way of writing Erlang programs.
The following is the code of a simple Erlang module, called circle and is saved in a file called circle.erl that calculates the circumference and the area of a circle, given its radius:
%%% Filename: circle.erl %%% Programmer: Mihalis Tsoukalos %%% Date: Saturday 25 May 2013 -module(circle). -export([length/1, area/1]). length(Radius) -> 2 * 3.1415 * Radius. area(Radius) -> 3.1415 * 3.1415 * Radius.
At the top of the module, the -module directive gives the name of the module, which is circle. The -export directive defines the list of functions that are visible to the world. The circle module exports two functions called length and area. Note that both the name and the total number of parameters of a function are defined.
Erlang considers functions with the same name but different number of parameters as different functions. All module code must be included in functions.
In order to compile the circle.erl file, you should execute the following command from the Erlang shell:
5> c(circle). {ok,circle}
If you now look inside the directory where circle.erl resides, you will also see a binary file called circle.beam, which is the compiled module code in BEAM format. Listing the available functions as well as other useful information about a module can be done using the following command:
7> circle:module_info(). [{exports,[{length,1}, {area,1}, {module_info,0}, {module_info,1}]}, {imports,[]}, {attributes,[{vsn,[187948631488141693241015230821467106334]}]}, {compile,[{options,[]}, {version,"4.9.1"}, {time,{2013,5,25,14,16,26}}, {source,"/Users/mtsouk/docs/article/Erlang.OSFY/circle.erl"}]}] 8>
Using its functions can be done as follows:
9> circle:area(1). 9.86902225 10> circle:length(1). 6.283 11> circle:length(2). 12.566 12>
If you use radius instead of Radius as the variable name, you will get the following warning message while compiling the circle module:
3> c(circle). circle.erl:8: Warning: this expression will fail with a 'badarith' exception circle.erl:10: Warning: this expression will fail with a 'badarith' exception {ok,circle}
This happens because, as mentioned earlier, variables start with capital letters in Erlang.
Last, if you want to use the area function from the circle module in another module that you create, you should include the following command inside the new module:
-import(circle, [area/1]).
There is no command to import all functions at once from an existing Erlang module.
Documenting Erlang code
Erlang allows you to include documentation during the module writing. You should always document your code not only to help other programmers who may read it, but even for yourself, because you may need to change or debug your code after some time. The full and documented version of the circle module, which is now called circleDoc, is as follows:
%% @author Mihalis Tsoukalos <tsoukalos@sch.gr> [http://users.sch.gr/tsoukalos] %% @doc This is a module that provides functions for calculating the length %% and the area of a circle. %% @reference For an article for Open Source for You. %% @copyright 2013, Mihalis Tsoukalos %% @version 1.0 -module(circleDoc). -export([length/1, area/1]). %% @doc Calculates the length of a circle given its radius %% using the 2 * pi * R mathematics formula. length(Radius) -> 2.0 * 3.1415 * Radius. %% @doc Calculates the area of a circle given its radius %% using the pi * pi * R mathematics formula. area(Radius) -> 3.1415 * 3.1415 * Radius.
In order to create the HTML files, you should execute the following command in the Erlang shell:
2> edoc:files(["circleDoc.erl"], [{dir, "doc"}]). ok
The output files are written in the doc directory. The contents of the doc directory are as follows:
$ ls -l doc/ total 64 -rw-r--r-- 1 mtsouk staff 2887 May 25 18:17 circleDoc.html -rw-r--r-- 1 mtsouk staff 57 May 25 18:17 edoc-info -rw-r--r-- 1 mtsouk staff 2109 May 25 18:17 erlang.png -rw-r--r-- 1 mtsouk staff 471 May 25 18:17 index.html -rw-r--r-- 1 mtsouk staff 420 May 25 18:17 modules-frame.html -rw-r--r-- 1 mtsouk staff 1070 May 25 18:17 overview-summary.html -rw-r--r-- 1 mtsouk staff 327 May 25 18:17 packages-frame.html -rw-r--r-- 1 mtsouk staff 895 May 25 18:17 stylesheet.css
Figure 1 shows the central HTML page of the created documentation for the circleDoc module whereas Figure 2 shows the detailed documentation for both module functions.
Creating something useful in Erlang
Now, lets use OTP to program a server process. The line numbers are added in order to better refer to the Erlang code and need not be typed. The name of the module is simpleServer. I deleted all comments from the code in order to make it smaller but I always put comments inside the code I write.
The code for the Erlang program is as follows:
1 %% Programmer: Mihalis Tsoukalos 2 %% Date: Monday 27 May 2013 3 4 -module(simpleServer). 5 -behaviour(gen_server). 6 -define(SERVER, ?MODULE). 7 -record(state, {count}). 8 9 -export([ 10 start_link/0, 11 stop/0, 12 say_hi/0, 13 get_count/0]). 14 15 -export([ 16 init/1, 17 handle_call/3, 18 handle_cast/2, 19 handle_info/2, 20 terminate/2, 21 code_change/3]). 22 23 start_link() -> 24 gen_server:start_link( 25 {local, ?SERVER}, 26 ?MODULE, [], []). 27 28 stop() -> 29 gen_server:cast(?SERVER,stop). 30 31 say_hi() -> 32 gen_server:cast(?SERVER,say_hi). 33 34 get_count() -> 35 gen_server:call(?SERVER,get_count). 36 37 init([]) -> 38 {ok, #state{count=0}}. 39 40 handle_call(get_count, _From, #state{count=Count}) -> 41 {reply, Count, 42 #state{count=Count+1}}. 43 44 handle_cast(stop, State) -> 45 {stop, normal, State}; 46 47 handle_cast(say_hi, State) -> 48 io:format("Hi!~n"), 49 {noreply, #state{count= 50 State#state.count+1} }. 51 52 handle_info(Info, State) -> 53 error_logger:info_msg("~p~n", [Info]), 54 {noreply, State}. 55 56 terminate(_Reason, _State) -> 57 error_logger:info_msg("terminating~n"), ok. 58 code_change(_OldVsn, State, _Extra) -> {ok, State}.
The key points of the simpleServer Erlang program are:
Line 5: This tells Erlang that the module will implement the gen_server behaviour. A behaviour pre-defines the mechanisms you will use to interact with processes. It is a way of formalising common patterns.
Line 6: This is a macro that instructs the Erlang compiler that every time it sees the word ?SERVER, it should replace it with ?MODULE. So, macros are simple text replacements.
Line 7: This line is for keeping count of the number of total calls made.
Line 31: This function prints a message on screen.
Line 23: This function calls the start_link function of get_server in order to start up the process. It is useful for testing purposes, because it makes you see if the program actually runs.
The following commands test the simpleServer server process:
1> c(simpleServer). {ok,simpleServer} 2> simpleServer:start_link(). {ok,} 3> simpleServer:get_count(). 0 4> simpleServer:get_count(). 1 5> simpleServer:get_count(). 2 6> simpleServer:say_hi(). Hi! ok 7> simpleServer:stop(). ok 24> =INFO REPORT==== 27-May-2013::12:27:43 === terminating 8> simpleServer:say_hi(). ok 9> whereis(simpleServer). undefined
The really interesting thing is that by writing Erlang code to supervise the server, when the simpleServer server goes down for some reason, the supervisor will start another simpleServer process!
The OTP supervisor code (simpleSup.erl) is as follows:
%% Programmer: Mihalis Tsoukalos %% Date: Monday 27 May 2013 -module(simpleSup). -behaviour(supervisor). -export([start_link/0]). -export([init/1]). -define(SERVER, ?MODULE). start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). init([]) -> RestartStrategy = one_for_one, MaxRestarts = 100, MaxSecondsBetweenRestarts = 3600, SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, Restart = permanent, Shutdown = 2000, Type = worker, AChild = {'simpleServer', {'simpleServer', start_link, []}, Restart, Shutdown, Type, [simpleServer]}, {ok, {SupFlags, [AChild]}}.
In order to test the supervisor code, I ran the following commands in the Erlang shell:
1> c(simpleSup). {ok,simpleSup} 2> {ok, Pid} = simpleSup:start_link(). {ok,} 3> unlink(Pid). true 4> simpleServer:start_link(). {error,{already_started,}} 5> simpleServer:get_count(). 0 6> simpleServer:get_count(). 1 7> simpleServer:say_hi(). Hi! ok 8> simpleServer:get_count(). 3 9> gen_server:cast(simpleServer, crash). =INFO REPORT==== 27-May-2013::13:02:04 === terminating =ERROR REPORT==== 27-May-2013::13:02:04 === ** Generic server simpleServer terminating ** Last message in was {'$gen_cast',crash} ** When Server state == {state,3} ** Reason for termination == ** {function_clause,[{simpleServer,handle_cast, [crash,{state,3}], [{file,"simpleServer.erl"},{line,44}]}, {gen_server,handle_msg,5, [{file,"gen_server.erl"},{line,607}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,239}]}]} ok 10> simpleServer:get_count(). 0 11> simpleServer:say_hi(). Hi! ok
As you can see, a new simpleServer process is running, so the supervisor did its job!
The Erlang process manager
Erlang also provides a process manager with a GUI that allows you to look into the current state of your processes and check what is happening. You can run it as follows:
1> pman:start().
You can see the process manager running in Figure 3.
This article was a small introduction to the Erlang programming language and I hope that it will be the beginning of your Erlang programming journey, while giving you a better understanding of Erlang. I cannot say that you will continue using it after reading this article but I am sure that if you do, Erlang will change you as a programmer and make you look at programs in a different way! Happy coding!
Web links and bibliography
[1] Erlang: http://www.erlang.org
[2] Try Erlang: http://www.tryerlang.org
[3] Standard ML: http://en.wikipedia.org/wiki/Standard_ML
[4] Learn You Some Erlang: http://learnyousomeerlang.com/content
[5] CouchDB: http://couchdb.apache.org
[6] Riak: http://basho.com/riak/
[7] Mnesia DBMS: http://www.erlang.org/doc/apps/mnesia/
[8] MochiWeb: https://github.com/mochi/MochiWeb
[9] Programming Erlang, 2nd Edition, Joe Armstrong, Pragmatic Bookshelf, ISBN 9781937785536, 2013, http://pragprog.com/book/jaerlang2/programming-erlang
[10] Erlang Programing, Simon Thompson and Francesco Cesarini, O Reilly, ISBN 978-0596518189, 2009.