A binary distribution of the Erlang system has been available for several
years. In late 1998, Ericsson announced the release of Erlang as
open source, under the Erlang Public Licence (a derivative
of the Mozilla licence). The release comprises full source for the
run-time system, compilers (both the original JAM and the new BEAM),
the Mnesia DBMS, libraries and utilities. It is available from the
erlang.org site [Erlang].
The features of open-source Erlang are described in a
White Paper [ADLM98], and in the references
listed above.
In additional, because the primary design focus was on distributed soft real-time systems, the language includes an extremely effective distribution and concurency mechanisms. Spawning a new process (thread) is extremely cheap and efficient, and spawning a process on another node is a trivial extension. Given a process identifier, all other communication and interaction with it is independent of whether it is local or remote. This greatly eases the implementation of robust distributed applications.
Experience with a number of large application developments seems to show that using Erlang leads to a faster implementation cycle and time to market, compared to using other more traditional development languages (see [AVWW96]).
There are of course, some disadvantages. Many numerical algorithms don't translate well into Erlang since they usually assume mutable data elements. The use of dynamic typing and garbage collection does involve some runtime overhead, though given the speed of current systems, in many applications this has not proved to be a problem. Also, because the original design focus was not on user interface issues, graphical interfaces may not be as efficient as in some other languages, though they certainly are available.
There is however, always the possibility of either calling out to an external program or using a linked in driver, which can be derived from code in any language. It comes down to a case of selecting the best language for any particular task. And in real, large scale, applications, this may often involve using several langauges for different components of the system (cf the AXD301 ATM switch later).
As well, the standard library includes a number of modules for applications monitoring and debugging, GUI interface support (using Tk widgets), parse tools, etc.
-module(bankserver).
-export([start/1]).
start(Sum) -> % start account server
register(bank,self()), % register as 'bank'
account(Sum). % process transactions
account(Sum) -> % transaction processing loop
receive % await transaction
{Pid, Ref, Amount} -> % account update msg received
NewSum = Sum+Amount, % update sum
Pid ! {Ref,NewSum}, % send response back
account(NewSum); % loop (recurse)
stop -> nil % end server msg received
end.
This could be started on some remote node specified by BankNode,
with a balance of 1000, and updated, as follows:
...
% spawn a bank account process with initial Sum 1000
Bank_pid = spawn(BankNode,bankserver,start,[1000]),
...
Ref = make_ref(), % make a unique ref value
Bank_pid ! {self(),Ref,17}, % send msg to server
receive % await reply from server
{Ref,New_balance} -> % reply says updated ok
...
end,
...
Alternatively, any other process in that node could communicate with
it using the registered name (which maps to the appropriate pid):
bank ! {self(),Ref,-12}, % send msg to server
...
Note that this example doesn't use any of the standard mechanisms
for programming and monitoring client-server applications, just
the core language features. It could be made much smaller and
more reliable using them (but then you wouldn't see what was
going on behind the scenes!)
Whilst probably too lean for use as is, this code could form the outline of code permitting configuration of some application using a web interface. Of course if serious web server is required, then a fully featured HTTPD/1.0 server is supplied as part of the Open Source Erlang system.
In the second Appendix is the code for a simple application monitor, which starts the requested application, catches and logs an errors that killed it, and then restarts the application. Again this is minimal code, and a fully featured monitor is provided as part of the system, but it illustrates how the process works. It could be used to monitor the above httpd server by being run as:
monitor:start(httpd,start,[]).
It could easily be extended to monitor an application on a remote node,
simply by providing an additional argument Node, and
then changing the spawning of the application to be remote using:
spawn_link(Node,Module,Function,Args)
The Mobility Server is an intelligent call control system incorporated within the Consomo PBX to provide a personal number service for mobile phone users. It comprises 486 Erlang modules with 230k loc (lines of code) and 20k loc in C for the device interfaces, and was written by a team of 35 people on time and under budget. It has now been sold to a number of customers.
The AXD301 ATM switch is a new asynchronous transfer mode (ATM) switching system which combines features associated with data communication, such as compactness and high functionality, with features from telecommunications, such as robustness and scaleability. The AXD 301 system is designed for nonstop operation, with duplicated hardware, and modular software, which can be upgraded to facilitate the introduction of new functionality without disturbing traffic.
There are a number of components in its application architecture, and different languages were used for each. All of the device interfaces and interaction with the switching fabric was written in C. The web based management interface was written in Java. The remainder of the application, including the overall control and management was written in Erlang. Each of these components comprise about 150k loc.
There are consistent reports that programming in a functional language such as Erlang is faster, results in smaller source code size, and is more maintainable than when traditional procedural languages are used. This is discussed in [Arms96].
%% httpd.erl - a simple HTTPD/0.9 web server in Erlang
-module(httpd).
-export([start/0,start/1,start/2,process/2]).
-import(regexp,[split/2]).
-define(defPort,8888). %% port to use if not given
-define(docRoot,"./htdocs"). %% HTML document root
%% start mini HTTPD/0.9 server, can specify port/docroot if wanted
start() -> start(?defPort,?docRoot).
start(Port) -> start(Port,?docRoot).
start(Port,DocRoot) ->
case gen_tcp:listen(Port, [binary,{packet, 0},{active, false}]) of
{ok, LSock} -> server_loop(LSock,DocRoot);
{error, Reason} -> exit({Port,Reason})
end.
%% main server loop - wait for next connection, spawn child to process it
server_loop(LSock,DocRoot) ->
case gen_tcp:accept(LSock) of
{ok, Sock} ->
spawn(?MODULE,process,[Sock,DocRoot]),
server_loop(LSock,DocRoot);
{error, Reason} ->
exit({accept,Reason})
end.
%% process current connection
process(Sock,DocRoot) ->
Req = do_recv(Sock),
{ok,[Cmd|[Name|[Vers|_]]]} = split(Req,"[ \r\n]"),
FileName = DocRoot ++ Name,
LogReq = Cmd ++ " " ++ Name ++ " " ++ Vers,
Resp = case file:read_file(FileName) of
{ok, Data} ->
io:format("~p ~p ok~n",[LogReq,FileName]),
Data;
{error, Reason} ->
io:format("~p ~p failed ~p~n",[LogReq,FileName,Reason]),
error_response(LogReq,file:format_error(Reason))
end,
do_send(Sock,Resp),
gen_tcp:close(Sock).
%% construct HTML for failure message
error_response(LogReq,Reason) ->
"<html><head><title>Request Failed</title></head><body>\n" ++
"<h1>Request Failed</h1>\n" ++ "Your request to " ++ LogReq ++
" failed due to: " ++ Reason ++ "\n</body></html>\n".
%% send a line of text to the socket
do_send(Sock,Msg) ->
case gen_tcp:send(Sock, Msg) of
ok -> ok;
{error, Reason} -> exit(Reason)
end.
%% receive data from the socket
do_recv(Sock) ->
case gen_tcp:recv(Sock, 0) of
{ok, Bin} -> binary_to_list(Bin);
{error, closed} -> exit(closed);
{error, Reason} -> exit(Reason)
end.
%% monitor - simple application monitor
-module(monitor).
-export([start/3,run/3]).
-define(max,5).
%% spawn of monitor process
start(Module, Function, Args) ->
spawn(?MODULE,run,[Module, Function, Args]).
%% start application being monitored with traps being caught
run(Module, Function, Args) ->
process_flag(trap_exit, true),
Child = spawn_link(Module,Function,Args),
monitor(Child, ?max, Module, Function, Args).
%% wait for something to go wrong, log, restart application
monitor(Child, N, Module, Function, Args) ->
receive
{'EXIT', Child, Why} when N > 0 ->
io:format("~p:~p~p died ~p~n", [Module,Function,Args,Why]),
NewChild = spawn_link(Module,Function,Args),
monitor(NewChild,N-1,Module,Function,Args);
{'EXIT', Child, _} ->
io:format('too many restarts on ~p:~p~p!~n', [Module,Function,Args])
end.