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