Wednesday, 31 July 2013

Erlang for Java Developers

You probably haven't noticed, but it's a couple of weeks since I last posted a blog. There is reason for this is that I've ruptured my Soleus and my leg is in a plaster cast. Being immobile I thought that it would be a good idea to investigate something totally different - it was either that or watch day time TV and, even though reruns of Kojak and Magnum PI were tempting, investigating Erlang came out tops.

The thing to remember here is that this is not an Erlang tutorial, the idea here is to examine a few of the similarities between Erlang and Java in order to try an provide a starting point for learning Erlang. If I've made any howling mistakes, then hopefully someone with more Erlang experience will let me know.

When getting started, the first thing they tell you about Erlang is that it's a functional language; however, before you have apoplexy at the thought, it's such a well structured functional language that you'd think you were dealing with objects.

What do I mean by that? In Java code is stored in files that represent classes, a class being a grouping of data and methods that perform a single responsibility. You instantiate a class and access its methods via a variable or, you can access its static methods via its class name.

In Erlang, code is stored in files called modules, with each module being a grouping of functions that perform a single responsibility. You cannot instantiate a module and instance variables and class variables do not exist; you can only use method argument variables. You access a method via its module name in a similar manner to accessing a Java static method. Like Java classes, Erlang modules have private and public functions.

Being a Java developer I was pleased to discover that there's an Erlang plugin for eclipse. This is because it's quicker to learn just a language rather than a language and a whole new set of development tools. A couple of Erlang consultants I spoke to a few months ago said they preferred to use emacs, and I did wonder why until I discovered that the eclipse plugin is rather flakey. Still it's good enough to get started with and it has got potential.

There are other similarities that should make any Java developer feel at home: Erlang module source files are compiled into .beam files which then run on the Erlang virtual machine; there's also eunit, Erlang's equivalent to JUnit, and a log4erl, which as its name suggests, is Erlang's version of Log4J. There's automatic document generation using edoc, Erlang's version of Javadoc and a standard project layout,which is very similar to a Maven layout that looks like this:


The structure is a little different to Maven: the target directory is called ebin and the src and test directories have been split at project directory level, but it's easy to follow and you get used to it.

From what I've said so far, you'd think that the biggest difference between Java and Erlang is that Java files have a .java extension and Erlang files have a .erl extension. Unfortunately, there's more to it than that, firstly there's the little matter or Erlang's weird looking syntax1

To investigate this I thought that I'd take my existing ShoppingCart and ShoppingCartTest classes and translate them into Erlang. These two classes are available in my telldontask project are look something like this...

The ShoppingCart Class


public class ShoppingCart {

 
private final List<Item> items;

 
private PaymentMethod method;

 
public ShoppingCart() {
   
items = new ArrayList<Item>();
 
}

 
public void addItem(Item item) {

   
items.add(item);
 
}

 
public double calcTotalCost() {

   
double total = 0.0;
   
for (Item item : items) {
     
total += item.getPrice();
   
}

   
return total;
 
}

 
public double calcTotalCost(double shipping, double minShippingAmount) {

   
double totalCost = calcTotalCost();
   
if (totalCost > minShippingAmount) {
     
totalCost += shipping;
   
}
   
return totalCost;
 
}

 
public void setPaymentMethod(PaymentMethod method) {
   
this.method = method;
 
}

 
public void pay(double shipping, double minShippingAmount) {

   
double totalCost = calcTotalCost(shipping, minShippingAmount);
    method.pay
(totalCost);
 
}
}

The ShoppingCartTest JUnit


public class ShoppingCartTest {

 
/**
   * Test method for
{@link tell_dont_ask.ask.ShoppingCart#getAllItems()}.
   */
 
@Test
 
public void calculateTotalCost() {

   
ShoppingCart instance = new ShoppingCart();

    Item a =
new Item("gloves", 23.43);
    instance.addItem
(a);

    Item b =
new Item("hat", 10.99);
    instance.addItem
(b);

    Item c =
new Item("scarf", 5.99);
    instance.addItem
(c);

   
double totalCost = instance.calcTotalCost();
    assertEquals
(40.41, totalCost, 0.0001);
 
}

}

The code above demonstrates some very basic shopping cart functionality; however, for more details on how these classes work, take a look at Defining Tell Don't Ask and Disassembling Tell Don't Ask.

The equivalent code in Erlang looks something like this:

The shopping_cart Module


-module(shopping_cart).

%% ====================================================================
%% API functions
%% ====================================================================
-export
([add_item/2,calc_total_cost/1,calc_total_cost/3,pay/3]).

%%
@doc Add an item to the order list
add_item
(OrderList,Item) ->
 
[Item | OrderList].

%%
@doc Calculate the total cost of all the items in a list. The List must have the following format:
%% 
[{itemName, Price}]
%%  where
%%  itemName -> atom
%%  Price ->
float()
calc_total_cost(OrderList) ->
  round_dp
(calc_total_cost(0,OrderList)).

%%
@doc Calculate the total cost of all the items in a list adding a shipping cost if the value is below a certain limit.
%% The Order List must have the following format:
%% 
[{itemName, Price}]
%%  where
%%  itemName -> atom
%%  Price ->
float()
calc_total_cost(OrderList,Shipping, MinShippingAmount) ->
  Cost = calc_total_cost
(OrderList),
  TotalCost = Cost + shipping
(Cost,Shipping,MinShippingAmount),
  round_dp
(TotalCost).

%%
@doc @todo Method not implemented
pay
(_Order,_Shipping, _MinShippingAmount) ->
  unimplemented.

%% ====================================================================
%% Internal functions
%% ====================================================================

calc_total_cost
(Result,[{_,Price} | TheRest]) ->
  calc_total_cost
(Result + Price,TheRest);
calc_total_cost
(Result,[]) ->
  Result.

shipping
(Cost,Shipping,MinShippingAmount) when Cost < MinShippingAmount ->
  Shipping;
shipping
(_,_,_) ->
 
0.

round_dp(Number) ->
  List = float_to_list
(Number,[{decimals,2}]),
  list_to_float
(List).

The shopping_cart_tests Module


-include_lib("eunit/include/eunit.hrl").

-module
(shopping_cart_tests).

%% ====================================================================
%% API functions
%% ====================================================================
-export
([]).

%%
@doc Calculate total code - written to match the Java style
calculate_total_cost_test
() ->
 
  EmptyList =
[],
  OrderList1 = shopping_cart:add_item
(EmptyList,{gloves,23.43}),
  OrderList2 = shopping_cart:add_item
(OrderList1,{hat,10.99}),
  OrderList3 = shopping_cart:add_item
(OrderList2,{scarf,5.99}),

  ?_assertEqual
(40.42,shopping_cart:calc_total_cost(OrderList3)).

%%
@doc Calculate total cost example - written in a better erlang style
calculate_total_cost_2_test
() ->

  OrderList =
[{gloves,23.43},{hat,10.99},{scarf,5.99}],
  ?assertEqual
(40.41,shopping_cart:calc_total_cost(OrderList)).

Experts tell me that you can do a lot more in fewer lines of code with Erlang. That isn't the case here, but then again I have added lots of comment lines (denoted by the '%' delimiter).

Compared to Java the code above looks pretty weird (it also looks a bit ugly as I don't have an Erlang to HTML converter). There aren't any instance variables so any data that's required is passed in as function arguments. If you take a look at add_item(…) you can see that it adds the Item variable to the head of a list, rather like: items.add(item) (Note that in Erlang variable names always begin with a capital letter).

Moving on to calc_total_cost() and things begin to look really strange… calc_total_cost(OrderList) is just a wrapper around calc_total_cost(0,OrderList). calc_total_cost(0,OrderList) is a call to either calc_total_cost(Result,[{_,Price} | TheRest]) or calc_total_cost(Result,[]), which are the functions that do the looping adding up the item prices from the list. Except that it doesn't loop; there aren't any for loops in Erlang, you have to use recursion, progressively adding prices in calc_total_cost(Result,[{_,Price} | TheRest]) and then recursively calling itself until the list is empty.

The thing about the Erlang syntax is that, although to a Java developer used to languages that are derivatives of C, it is very logical and therefore easy to pick up.

One thing to note is that the Erlang above has been written to mimic Java. It is probably not the way you'd approach the development of an Erlang shopping cart from scratch.

Why would you choose Erlang over Java? Certainly not for its similarities with Java. You'd choose Erlang over Java when its features and benefits help you to solve your problem(s) more efficiently and cost-effectively. According to Lenart Öhman's Google Tech Talk Erlang target's applications that need to be:

  • fault tolerant
  • non-stop
  • concurrent
  • distribuite, scalable and heterogeneous
  • soft real-time
  • require "prototypeability"

These goals have been achieved in a number of ways. For example, message passing between processes is part of the language rather than being a separate API. To send a message to another process you simply type:

  Pid ! theMessage,

…where Pid is the id of the process that'll receive the theMessage. To send a message another process running on a different Erlang virtual machine you type:

  Pid ! theMessage,

From this unfunny joke my may have guessed that processes on Erlang virtual machines are location transparent. This means that it makes no different to Erlang processes which machine they're running on; whether it's locally or on different physical hardware. This is because Erlang virtual machines can talk to each other and can be clustered; something way beyond the JVM.

To receive a message you use the Erlang keyword: receive, something like this:

%% @doc Receive a message and print the contents
print
() ->
  receive
    Message ->
      io:format
("The message is: ~p~n",[Message])
 
end.

Processes themselves are very lightweight, designed to make use of hyper threading on mega multi-core processors in a way that other languages can't emulate. Processes are very important in Erlang, I read somewhere that if Java is an Object Oriented Language, then Erlang is a Process Oriented Language.

If this blog reads like an advert for Erlang then that's because, after dipping my toe in the water, I can see that's there are a number of problems that it can solve on my current project more cheaply and with less hassle than a Java based solution. There are, on the other hand, those problems where Java and Spring - the usual subject of this blog - is better suited. Computer languages are simply tools and you should aways pick the best one for the job in hand.


1Weird looking if you're a Java programmer as its based on Prolog and not C.
The Java code samples for this blog are available from the Captain Debug Github repository: https://github.com/roghughe/captaindebug, whilst the Erlang code is available from my Erlang Samples Github repository: https://github.com/roghughe/erlang_samples.


1 comment:

nickman said...

Brilliant. I'm not sure that I want to start coding in erlang, but this was a great ecumenical comparison. Nice work.