Culture shock

Kent Boortz kent@REDACTED
Sat Dec 2 18:16:47 CET 2000


> Tail recursion looks uncannily like the infamous goto statements, where
> calling functions comes to look a lot like jumping around labels.  How
> do you keep the pasta out of Erlang code?

Most Erlang program I have read written by skilled Erlang programmers
don't have the above problem so I think it is possible to keep the
"pasta" out of Erlang code.

But this is a bit of a problem for languages like Erlang and Prolog,
you find yourself spending lots of time trying to find clever names
for the "extra functions" you have to use for "iterations inside
iterations". The lazy approach creating function names for "iterative
sub functions" is by adding a number to the "main" function. This sure
doesn't improve the readability. A good name for a "sub function" can
improve the readability for the program compared to an iterative
language. Still, I find this to be a problem and there are several
ways to address it.

The list module contains several iterators that take a data type 
"functional objects" (Funs) as an argument. A iteration like

    .
    .
    foo(List, []), % Function call to start new iteration level
    .


  foo([], Acc) ->
    Acc;
  foo([H | T], Acc) ->
    <some code that use H to modify Acc to NewAcc>
    foo(T, NewAcc).

can be replaced by an inline solution

    .
    .
    Fun = fun(H, Acc) ->
             <some code that use H to modify Acc to NewAcc>
             NewAcc
          end,
    lists:foldl(Fun, [], List).
    .

Also you tend to write more "generic" help functions in Erlang and
there are some in the Erlang standard library for common "generic
behaviors". Many Erlang programs uses the same base loop, something
like

  loop(State) ->
    receive
      {message_a, Data, Sender} ->
         <do something, modifying State to NewState>
         Sender ! {reply_a, .....}  % send back a reply
         loop(NewState);
      {message_b, Data, Sender} ->
         <do something, modifying State to NewState>
         loop(NewState);    % no reply to this message
      .
      .
    end.

Using the generic behavior support the same code may look like  

  -behaviour(gen_server).

  .
  .

  handle_call({message_a, Data}, State) ->
      <do something, modifying State to NewState>
      {reply, {reply_a, .....}, NewState};

  handle_call({message_b, Data}, State) ->
      <do something, modifying State to NewState>
      {noreply, NewState};

      .
      .

The disadvantage with using Funs and "generic behaviors" is that they
sometimes makes the code even harder to understand. But the advantage
is often higher. Using Funs and the list iterators the right way makes
loops easier to read and the generic behaviours in the standard
library adds values like support for runtime code upgrades, debugging
etc.

Ref:

     http://www.erlang.org/doc/r7b/doc/extensions/funs.html
     http://www.erlang.org/doc/r7b/doc/design_principles/part_frame.html
     http://www.erlang.org/doc/r7b/lib/stdlib-1.9.1/doc/html/gen_server.html

kent



More information about the erlang-questions mailing list