[erlang-questions] Re: The If expression
James Aimonetti
james.aimonetti@REDACTED
Thu Apr 22 16:28:03 CEST 2010
This thread is well-timed as I have an issue I wanted some help on.
I have two lists, one of keys and one of values. I want to zip them
together, but I have one specific key/value combo that I want to exclude
from the zipped list. For context, this is creating an appropriate
{proplist} for couchbeam.
to_couchbeam(Fields, Values) ->
{ lists:flatten( lists:zipwith(fun to_couchbeam_zipper/2, Fields,
Values) ) }.
to_couchbeam_zipper(F, V) ->
K = to_couchbeam_key(F),
Val = to_couchbeam_value(V),
case Key == <<"_rev">> andalso Val == undefined of
true -> [];
false -> {K, Val}
end.
to_couchbeam_* converts various terms to formats suitable for couchbeam
to save the document. I only want to remove the '_rev' key when it's
value is undefined, otherwise couchbeam fails to save the document. If
'_rev' has a defined value, great, keep it in the generated proplist.
I refactored according to some of the advice earlier, so now
to_couchbeam_zipper is
to_couchbeam_zipper('_rev', undefined) -> [];
to_couchbeam_zipper(F, V) -> { to_couchbeam_key(F), to_couchbeam_value(V) }.
So I may have answered my own question here, as concerns the case
statement. Is there a cleaner way to zip two lists in this fashion
without resorting to inserting an empty list for unwanted k/v pairs and
flattening the resulting proplist?
Thanks,
James
Jay Nelson wrote:
> 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
>
>
> ________________________________________________________________
> erlang-questions (at) erlang.org mailing list.
> See http://www.erlang.org/faq.html
> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
>
>
--
James Aimonetti
mobile: 314.809.6307
work: 540.459.2220
email: james.aimonetti@REDACTED
website: http://jamesaimonetti.com
More information about the erlang-questions
mailing list