About Erlang programming language

This article describes various properties of Erlang programming language, preferably with a short code example.

Functional (Higher order functions) :

Erlang supports higher order functions, that is, functions which accept functions as argument or return functions as return value. In Erlang functions are first class citizens and are treated similar to integers, floats or strings. (Note: Soon recursive anonymous functions would be supported.)

	1> lists:map(fun(X) -> X*X end, lists:seq(1,5)).
	2> lists:map(fun(X) -> (X rem 2) =:= 0 end, lists:seq(1,5)).
	3> lists:filter(fun(X) -> (X rem 2) =:= 0 end, lists:seq(1,5)).
	4> lists:sum(lists:seq(1,5)).
	5> lists:foldl(fun(X, Y) -> X + Y end, 0, lists:seq(1,5)).
Exact Integer Arithmetic :

Similar to most functional languages Erlang supports exact integer arithmetic. Thus programmers do not have to worry about overflow errors.

	1> 999999999999999 * 999999999999999.
	2> 9999999999999999999999999 rem 2.  
	3> 9999999999999999999999999 div 2.
Dynamic typing

Erlang is dynamically typed. Thus programmers do not need to declare type of the variables. Note that type checking using success types is possible using dialyzer, which can be further augmented by -spec() declarations.

	1> A=fun(X, Y) -> X + Y end.
	2> A(1, 2).
	3> A(1.1, 2.2).

Erlang supports guards in function definition, case, try-catch etc. which make code more reliable and easier.

	1> A=fun(X, Y) when is_integer(X), is_integer(Y) -> X div Y end.
	2> A(3, 2).
	3> A(10, 7).  
	4> A(20, 7).
	5> A(10.1, 7).
	** exception error: no function clause matching erl_eval:'-inside-an-interpreted-fun-'(10.1,7)
	6> A(10, 7.1).
	** exception error: no function clause matching erl_eval:'-inside-an-interpreted-fun-'(10,7.1)
Strong match syntax

Strong match syntax support in function declaration, statements, receive, try-catch, etc. makes code compact and easy to write.

 	1> [{A, B}, C, [D, E]] = [{a, b}, c, [d, e]].
	2> A.
	3> [A, B, C, D, E].
Immutable memory

It is not possible to change binding (value) of identifiers (variables) in Erlang. In fact '=' is not an assignment operator. It is a match operator.

	1> X=1.
	2> X=2.
	** exception error: no match of right hand side value 2
Concurrency oriented

Erlang supports spawn() function for creating new processes as part of language without requiring use of external library. This makes it very easy to write concurrent applications in Erlang. Further applications are largely free from dead-lock and race conditions due to immutable memory property as there is no need for a critical section.

	1> Self=self().
	2> spawn(fun() -> Self ! 3 * 4 end).
	3> flush().
	Shell got 12
Pure message passing

Erlang processes communicate only via pure message passing. Shared memory is not supported. Note that other mechanisms for processes to share information such as ets, dets, mnesia, files or sockets are present. If these mechanisms are used then in most cases isolation and atomicity options are provided or are inherently available due to pure message passing.

	1> ets:new(table1, [set, protected, named_table]).
	2> ets:insert(table1, {hello}).
	3> ets:lookup(table1, hello).
	4> ets:insert(table1, {hello, 5}).
	5> ets:lookup(table1, hello).     
	6> Self=self().  
	7> spawn(fun() -> Self ! ets:lookup(table1, hello) end).
	8> flush().
	Shell got [{hello,5}]
Easy distribution

Erlang makes it very easy to write distributed programs due to following properties:

Cluster support :
It is possible to connect two or more Erlang nodes running on same or different machines just by specifying a name (or sname) and same cookie value. Thus there is no effort required in socket programming, marshalling, demarshalling etc. to make two nodes communicate.
Location transparent processes :
All operations that can be performed on local processes (message passing, linking, etc.) can also be performed on processes located on other nodes. Thus once a process identifier (Pid) is known the location of process does not affects the operations or interactions that can be performed with the process.
Linking distributed processes :
As a special case of location transparent processes, one can even link to processes spawned on other nodes. Thus if processes on remote node die, a message is received by Erlang system as a notification. This can be very useful in writing reliable distributed application so that if a process on remote node dies or if the remote node itself dies, then a corresponding process can be spawned on current machine to resolve the issue.

Example code for adder.erl:

adder() ->
	{add, X, Y, Sender} ->
	    Sender ! {sum, X+Y},
	stop ->
start() ->
    Pid1=spawn(fun adder/0),
    register(adder, Pid1),

add(Pid1, X, Y) ->
    Pid1 ! {add, X, Y, Self},
	{sum, Sum1} ->
Steps performed on node1:
	[saurabh@barjatiyacc about_erlang]$ erl -sname node1
	Erlang R14B04 (erts-5.8.5) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
	Eshell V5.8.5  (abort with ^G)
	(node1@barjatiyacc)1> c(adder).
	(node1@barjatiyacc)2> adder:start().
	(node1@barjatiyacc)3> adder:add(adder, 3, 4).
	(node1@barjatiyacc)4> whereis(adder).
Steps performed on node2:
	[saurabh@barjatiyacc about_erlang]$ erl -sname node2
	Erlang R14B04 (erts-5.8.5) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]

	Eshell V5.8.5  (abort with ^G)
	(node2@barjatiyacc)1> Pid1=rpc:call('node1@barjatiyacc', erlang, whereis, [adder]).
	(node2@barjatiyacc)2> Pid1 ! {add, 3, 4, self()}.
	(node2@barjatiyacc)3> flush().
	Shell got {sum,7}
	(node2@barjatiyacc)4> rpc:call('node1@barjatiyacc',adder,add,[adder, 5, 7]).
	(node2@barjatiyacc)5> process_flag(trap_exit, true).
	(node2@barjatiyacc)6> link(Pid1).
	(node2@barjatiyacc)7> Pid1 ! stop.
	(node2@barjatiyacc)8> flush().
	Shell got {'EXIT',<6515.45.0>,normal}
Database server: mnesia

A database server written in Erlang which supports all erlang data types (Lists, Tuples, etc.) directly is available. Further the database supports distribution across nodes for scalability and redundancy. It is possible to start database only in RAM to use it as transactional memory.

Example (Uses records from mnesia.hrl):
	1> rr("mnesia.hrl").
	2> #student{}.
	#student{id = undefined,roll_no = undefined,
        name = undefined,email = undefined}
	3> A1=#student{id=1, roll_no=200899004, name="Saurabh Barjatiya", email="saurabh@sbarjatiya.com"}.
	#student{id = 1,roll_no = 200899004,
        name = "Saurabh Barjatiya",email = "saurabh@sbarjatiya.com"}
	4> mnesia:start().
	5> mnesia:create_table(student, [{attributes, record_info(fields, student)}, {index, [roll_no]}, {type, set}]).
	6> F1=fun() -> mnesia:write(A1) end.
	7> Query1=qlc:q([X || X<-mnesia:table(student)]).
	8> F2=fun() -> qlc:e(Query1) end.
	9> mnesia:transaction(F2).
	{atomic,[#student{id = 1,roll_no = 200899004,
              name = "Saurabh Barjatiya",
              email = "saurabh@sbarjatiya.com"}]}
	10> A2=A1#student{name="Other student"}.
	      #student{id = 1,roll_no = 200899004,name = "Other student",
              email = "saurabh@sbarjatiya.com"}
        11> F3=fun() -> mnesia:write(A2) end.             
	12> mnesia:transaction(F3).             
	13> mnesia:transaction(F2).             
	{atomic,[#student{id = 1,roll_no = 200899004,
        name = "Other student",email = "saurabh@sbarjatiya.com"}]}
Web server: yaws

A very scalable web server written purely in Erlang is available. This again allows message passing from web server or web applications to other applications without impedance mismatch. An online Erlang interpreter which uses this to its advantage is available here.

	This web page is being served using yaws :)
Supporting tools and libraries

A large number of tools and libraries which make it easy to program using erlang are available such as:

Most of these tools support concurrent and distributed programs. Thus debugging or type checking distributed programs is equally easy using same tools which can be used for debugging sequential programs.

Concise and arguably beautiful code

Code written in Erlang is very concise and it is pleasure to read it. Most the times there is one-to-one correspondence between Erlang code and algorithm explained in English, provided enough supporting functions are available for necessary abstraction.

Example factorial module:

factorial(0) ->
factorial(N) ->
    N * factorial(N-1).
Example usage of factorial module:
	1> c(factorial).
	2> factorial:factorial(100).
List comprehension

List comprehension make it very easy to write both map and filter operations in very easy and concise manner.

Example quicksort.erl module written using list comprehensions is:

sort([]) ->
sort([H | T]) ->
      [sort([X || X<-T, X==H])
Example usage of quicksort module:
	1> c(quicksort).                                                                  
	2> quicksort:sort(lists:map(fun(_) -> random:uniform(1000) end, lists:seq(1,10))).

Escript allows one to write directly usable scripts in Erlang which do not require manual compilation to beam code for execution.

Example hello_world.escript script:
#!/usr/bin/env escript

main(_) ->
    io:format("Hello world~n"),
Example invocation of hello_world escript:
	[saurabh@barjatiyacc about_erlang]$ ./hello_world.escript 
	Hello world
	[saurabh@barjatiyacc about_erlang]$ 
Collection primitive types: Lists and Tuples

Similar to most functional languages lists ([1, 2, 3]) and tuples ({1, 2, 3}) are available as primitive data types. This makes it very easy to write programs which deal with collections without requiring external libraries or effort in implementing complex data structures.

	1> {ok, A1} = file:read_file("mnesia.hrl").
	{ok,<<"-record(student, {id, roll_no, name, email}).\n">>}
	2> A2=binary_to_list(A1).
	"-record(student, {id, roll_no, name, email}).\n"
	3> A3=string:tokens(A2, "-(, {}).\n").
	4> length(A3).
	5> lists:usort(A3).
	6> lists:reverse(A3).
Support for several thousand processes

We can spawn hundred thousand or more processes. Erlang uses only 200-300MB RAM in creating and keeping these many processes blocked on receive.

Example (Start erlang using something like 'erl +P 10000000'):
	1> Pid_list=lists:map(fun(_) -> spawn(fun() -> receive stop -> ok end end) end, lists:seq(1,100000)). 
	2> erlang:memory().           
	3> length(erlang:processes()).
	4> lists:map(fun(Pid1) -> Pid1 ! stop end, Pid_list).
	5> length(erlang:processes()).                       
	6> erlang:memory().                                  
Reliability : OTP

Since erlang processes can monitor each other a very reliable framework which allows programmers to monitor their programs (servers) such that if programs crash they can be restarted is part of Erlang as OTP. A very reliable switch AXD 301 which has about a million lines of Erlang code is said to have nine nines of availability. (About 1 second downtime in 40 years).

The interpreter application available here uses OTP internally.

Distributed databases: Riak and CouchDB

Very reliable and scalable distributed databases such as Riak and CouchDB are built using Erlang. This makes Erlang very good choice for big data applications. The advantages of using Riak and CouchDB from Erlang is same as those for using Mnesia or yaws.

Interpreter application available here uses mnesia to persistently store environment bindings for users between yaws server restarts. This allows me to restart yaws on server without worrying about a connected user loosing his session information between reboots.

Binaries and Bit-match syntax

Erlang supports binaries and supports matching of bits. This makes it very suitable for network protocol programming, image processing, reading encoded files, etc.

	1> Red1=30, Green1=15, Blue1=6.
	2> Color16_1 = <<Red1:5, Green1:6, Blue1:5>>.
	3> <<Red2:5, Green2:6, Blue2:5>>=Color16_1.
	4> {Red2, Green2, Blue2}.
	5> Binary1 = <<1, 2, 3, 6, 7.8/float>>.
	6> Binary2 = <<$a, $b, Binary1:2/binary, $c, $d>>.
	7> Binary3 = <<"Hello", "World", <<32, 97, 98, 99>>:2/binary >>.
	<<"HelloWorld a">>
	8> Binary4s = {<<1:32/big>>, <<1:32/little>>, <<1:32/native>>,<<1:32>>}.
	9> Binary5s = {<<-2:32/signed>>, <<-2:32/unsigned>>, <<66535:16/signed>>, <<66535:16/unsigned>>}.
Active once sockets

Erlang has very strong support for TCP/IP sockets. This makes it very good choice for building network applications. One unique feature of erlang is support for active once sockets where a socket automatically becomes inactive after receiving data. This way socket gets blocked and does not accepts more information unless application has consumed existing information. This protects application from getting flooded with data without having to write lot of code to enable/disable socket as part of application logic.

Priority receive with timeout

All erlang processes talk to each other using message passing. The messages received by each process are queued in message queue till process uses receive call to retrieve messages from its queue. The receive function supports match to allow matching if specific messages first allowing messages to be processed in different order in comparison to order in which they were received. receive also supports timeout so that if there is no matching message, program can do something else instead of remaining blocked forever.

	1> self() ! 1.
	2> self() ! 2.
	3> receive 3 -> 3 after 0 -> empty_queue end.
	4> receive 2 -> 2 after 0 -> empty_queue end.
	5> receive 2 -> 2 after 0 -> empty_queue end.
	6> receive 1 -> 1 after 0 -> empty_queue end.
	7> receive 1 -> 1 after 0 -> empty_queue end.