[erlang-questions] Erlang elseif

Jay Nelson <>
Sat Nov 22 08:13:49 CET 2008


kdronnqvist wrote (I elided much of the original to focus on specific  
points):

 > The thing is that I want a general way to branch the execution
 > of my code based on the result on an expression.

 > the logic in what I'm trying to do is really REALLY not that
 > complicated.

When I am writing C or Java or other procedural languages, I think
in terms of if ... else and serial short-circuiting code.  That is  
what the
language family promotes.

When I am writing functional code, I think in terms of whole sets or
lists being filtered.  A parallel set of tests is more akin to the  
mindset
rather than a series of single steps, and branching is not a concept
that I rely on.  Instead I chain together functions in different orders
depending on the pattern of the result that is returned.

When I code in erlang, I think of 'case' as an exclusive set of options
that are essentially considered in parallel and one is chosen as the
path through the code.  The other alternative that comes to mind is
to run all tests and filter the results.  These are just more natural  
ways
which the language promotes.  I don't worry about efficiency until I
have working code which proves itself to be too slow.

My first impression of your question is that you are thinking in terms
of a procedural paradigm rather than a functional paradigm.  My first
inclination was a list comprehension, and reviewing your proposals I
saw that your last option was on the right track, but a bit verbose:

--------------------------------------------------

%% The list comprehension way
elseif4(A) ->
     case [begin
               {Name,_} = X,
               Name
           end
           || X <- [{caps, ?CAPS},
                    {small, ?SMALL},
                    {nums, ?NUMS}],
              begin
                  {_,List} = X,
                  lists:member(A,List)
              end] of
         [caps] ->
             io:format("Member of capital\n");
         [small] ->
             io:format("Member of small\n");
         [nums] ->
             io:format("Member of nums\n");
         _ ->
             io:format("Not a member\n")
     end.

--------------------------------------------------


In my mind, you are combining two concepts: the tagging of a
condition, and the printing of a result.  You may need the tagged
value for other reasons.  Here's my approach:


get_type(Char) ->
     TestPairs = [{"QWERTY", capitals},
		 {"qwerty", smalls},
		 {"123456", nums}],
     [Type || {CharSet, Type} <- TestPairs, lists:member(Char,  
CharSet)].

print_type(Char) ->
     [Type] = get_type(Char),
     io:format("Member of ~w~n", [Type]).


get_type/1 is a tagging function which uses a list comprehension
generator followed by lists:member/2 as a filter.  The shorthand
is to do a "destructuring bind" for each element passed out of
the list.  Filters can be any function or expression which returns
true or false, skipping any elements which return false.

This function can be used for disjoint tests or overlapping tests
-- in either case you get a set of valid tags for the Char passed
in (or [] if no tag applies).  Here, I explicitly expect one matching
tag type and specify that with the [Type] pattern.  A case statement
could be used instead:

case get_type(Char) of
    [] -> io:format("Unknown char type");
    [Type] -> io:format("Member of only one set: ~w~n", [Type]);
    MoreThanOneType -> io:format("Member of sets: ~w~n",  
[MoreThanOneType])
end.

print_type/1 uses the result of tagging to print your desired result.
The composition of tagging followed by tag analysis is a typical
functional chaining of tasks.

If you look at this code, the concept of branching on a condition
is not represented.  While what you wanted to do is conceptually
simple, the serial branching technique does not occur to me as
an approach when coding erlang.

jay




More information about the erlang-questions mailing list