Erlang Central

Hardcoding Expectations

Revision as of 07:22, 13 June 2007 by Rvg (Talk | contribs)

Author

Rudolph van Graan

Article

One of the strangest things I had to get used when I started programming in Erlang was to write correct programs (compared to programs that handled all cases). This is one of the fundamental rules in Erlang.

If you look at the code from Nested Cases, you can see through inspection that the function can return one of two possible values: null or {ok,Value}

 1: function(Operation,Values) ->
 2:   case Operation of
 3:     null -> 
 4:       null;
 5:     multiply ->
 6:       do_multiply(Values)
 7:   end.
 8: 
 9: do_multiply([Value1,Value2]) when integer(Value1),
10:                                   integer(Value2)->
11:   {ok,Value1*Value2}.

The return values for all other input values are not defined. If you write code that uses this function, you might be tempted to write something like this:

 1: main() ->
 2:   Value1 = 10,
 3:   Value2 = 20,
 4:   case function(multiply,[Value1,Value2]) of
 5:     null ->
 6:       {error,unexpected_null};
 7:     {ok,Value} ->
 8:       ... ;Rest of code here
 9:     SomethingElse ->
10:       {error,{unexpected,SomethingElse}}
11:   end.

Note lines 5 and 9. This is in principle very bad code. Keep in mind that you expect the multiply operation to actually return {ok,Value}, the rest doesn't mean anything. In reality you are now forcing the code calling this function to understand details about deeply nested functions. Because you *know* that the multiply funtion will succeed (It is one of your assumptions), it is much better to write:

1: main() ->
2:   Value1 = 10,
3:   Value2 = 20,
4:   {ok,Value} = function(multiply,[Value1,Value2]),
5:   ...

This way, you make it explicitly clear that you expect function(...)to work and to return something like {ok,Value}. If it does not, the function call wil crash and give you a useful diagnostic dump.