The If expression

Jay Nelson jay@REDACTED
Thu Apr 22 06:59:46 CEST 2010


Henning Deidrich offered one-armed ifs to rewrite:

I never use the if statement.  It doesn't even occur to me to use it,  
although writing Java or something else I use it all the time  
naturally.  Here's the style I would use for some of your cases (some  
of the others may not have enough context to be good examples for  
rewriting):

 > (2)
 >        verbose_echo(Verbose, Format, Para) ->
 >
 >            % Don't print if verbosity is off (false).
 >         >>> if Verbose -> echo(Format, Para); true -> nil end.

verbose_echo(true, Format, Para) ->
     echo(Format, Para);
verbose_echo(_, _Format, _Para) ->
     nil.

It doesn't do what the comment says, and you have to consider whether  
nil is the value you want back.  It depends on what echo/2 returns as  
to what is appropriate (and whether the caller even cares about the  
return value).  With this approach it is easier to test and easier to  
add new cases (verbosity levels) or change the existing one.


 >    (3)
 >            % If exists, drop old before adding new element
 >            Prev = gl:get(suite, Suite),
 >         >>>    if Prev /= undefined -> gl:drop(suite, Suite); true ->
 >        nil end,
 >            gl:add(suite, Suite),

Prev = case gl:get(suite, Suite) ->
     undefined -> nil;
     Defined -> gl:drop(suite, Suite), Defined
end,
gl:add(suite, Suite),


Here I made the assumption that you needed the value of Prev later.   
If you don't, you can drop it out and not care what the return value  
of the case statement is.

This is actually a common code smell to watch for.  You are setting a  
variable, then testing for a negative value using /= constant.   
Instead, use case and make the first clause the constant you want to  
exclude, everything else will match the catch-all clause.

Even when I have case ... of true -> ...; false -> ... end   I find  
it more comforting in erlang to know I covered the cases I care  
about.  You can even use a single armed case if you want a freak  
value to crash your process.

 > (5)
 >      safe_unregister(Name) ->
 >      Registered = whereis(Name) /= undefined,
 >       >>> if Registered -> unregister(Name); true -> nil end.

safe_unregister(Name) ->
   case whereis(Name) of
     undefined -> ok;
     Other -> unregister(Name), ok
   end.

Another instance of #3.  You can easily fix the return values to  
different ones, or have a return value after the case ... end.  If  
you need to know the value of whereis, you can modify it to this:

safe_unregister(Name) ->
    Loc = case whereis(Name) of
        undefined -> undefined;
        Other -> unregister(Name), Other
    end,
    ... computations involving Loc ...
   %% or even return it...
   Loc.


Any time you find yourself binding a variable, and then soon after  
wanting to modify it or do an if based on a small set of values,  
stop, back up and try using a case statement.  It has the advantage  
of giving you multiple variables with delayed binding of the correct  
one as your chosen value:

Chosen = case multi_var_fun() of
           Val1 -> Val1;
           Val2 -> f(Val2);
           Val3 when Val3 > 0, Val3 < 5 -> g(Val3);
           Other -> h(Other)
end,

Here Chosen is a late-bound variable with several values visited as  
intermediate bindings before arriving at a final choice.  An  
imperative programmer tries to do something like:

Chosen = multi_var_fun(),
if (Chosen == Val1) ...
else ...

The erlang way is to avoid binding the variable you care about until  
you are absolutely sure you have the _value_ you care about, rather  
than modifying the value along the way.   A one-armed if is  
semantically like saying I want Val1 most of the time, and in a few  
cases I want to change it to a different value.

jay



More information about the erlang-questions mailing list