[erlang-questions] The If expression

Henning Diedrich hd2010@REDACTED
Thu Apr 22 03:08:53 CEST 2010


Hi Richard,

thanks a lot for the lightning-speed & sorrow reply!

Please be gentle, my eyes are sore already :-D

I also realize I was somewhat hysteric, maybe.

Looking for examples now, I found 5 true->nil in a thousands lines. So 
maybe that absolves a bit of my guilt of doing imperative code. No 
matter, the question remains as sincere as before. That was exactly what 
I wanted to address and what the 5 places - given below - totally look like.
>
>> Did anything come from the discussion about the if expression ( 
>> http://www.erlang.org/pipermail/erlang-questions/2009-January/040808.html )? 
>>
>
> (1) The "undocumented?" comment in that article is just plain wrong.
I meant to basically give the head of that related thread.
>     (B) General Erlang "style" for 'else' is '; true ->'
It's not hard to get used to.  I wasn't going to complain about that 
oddity actually, but wondered what to do different, to not have to write 
it 'empty' now and then.

Regarding 'have', see below please.
>     (C) It has been argued at great length over many years in the
>         Software Engineering community that 'boolean' is almost always
>         the wrong type to use.  In too many situations, it is not clear
>         which convention is followed: does 'true' mean connected or
>         disconnected?  In too many other situations, there are more than
>         two possibilities.  In this mailing list, the latter case has
>         very often been substantiated.
I think it's exactly the (few) cases where 'yes or no' is what matters, 
see samples at bottom, thanks.

The rest of the paragraph, I won't dispute. AND I'd happily avoid the 
(few) cases left.
>> I remember reading that one-branched ifs shouldn't be necessary.
> (3) It's not that they SHOULDN'T be necessary.
>     It's not even the obvious fact that they AREN'T necessary.
>     The point is (2,C) that they are often WRONG.
Right, it sure looks like they /are/ necessary, to me. Which /is/ why I 
started wondering if I am doing sth. wrong here.
>>
>> Is there a specific tip on how to think differently when one runs 
>> into frequently using one-armed ifs? I.e., writing a lot of true -> 
>> nil for the empty second branch?
>
> For one thing, this is a clear sign of writing imperative code.
Exactly, that's why I am asking about /thinking/ differently.
> Pure functional languages like Haskell and Clean don't have one-armed
> ifs because every expression has to have a value, and both arms of an
> if have to have values of the same type.
It's funny you say that because it seems like /all/ spots where I ran 
into this issue are one of

    * output
    * message sending
    * global names registration

... David's example adds

    * creating a connection

... also an external state issue.

All samples seem to be about /creating a side effect/ ---  or, under 
certain conditions, not creating it.

Is that a legitimate opening for a one-branched if? Exactly for the less 
functionally-clean stuff, on Erlang's more practical leanings?

As I said, the rest of the 1000 lines, with ~15 more ifs, didn't need 
empty 'else' clauses. Way rare enough to be a non-issue as matter of 
style, typing or readability. But for the principle.
>
> The best thing to do is to show us some code and invite us to refactor 
> it.
Thanks a lot for the offer! With a tad of context. Sorry if there should 
be a typo, I sanitized them to make them more concise.

    (1)

                print ->
                    if
                     % suppress printing the default suite when unused
         >>>          (Nom == "Default") and (Passed+Failed+Crashed == 0) ->
         >>>                 nil;

                        % print all other
                        true ->
                            echo("~s~n~s ~s - Tests: ~p, Passed: ~p,
        Failed: ~p, Crashes: ~p~n~s",
                                 [Line, Nom, Verdict, Passed+Failed,
        Passed, Failed, Crashed, Line])
                    end,

                    main ! { printed, self() },

                    suite_loop(Nom, Caller, Sub, Passed, Failed, Crashed);

    (2)


        verbose_echo(Verbose, Format, Para) ->

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

    (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),

    (4)


        glist() ->

            spawn(fun() -> glist_loop([], nil, nil) end).

        glist_loop(List, PrevCaller, Return) ->

            % call back to originally caller, to deliver result.
            % (so: not on very first call, if no Caller is given, or it
        died (?) in the meantime.
            if
                 is_pid(PrevCaller) ->
                    vecho(?D4, "call ~p ~p", [PrevCaller, Return]),
                    PrevCaller ! Return;
         >>>     true -> nil
            end,

            receive
                { add, Element, Caller } -> glist_loop([Element | List],
        Caller, ok);
                { drop, Key, Caller } ->
        glist_loop(lists:keydelete(Key,1,List), Caller, ok);
                { get, Key, Caller } -> glist_loop(List, Caller,
        lists:keyfind(Key,1,List));
                E -> throw(E)
            end.

    (5)


        safe_unregister(Name) ->

            Registered = whereis(Name) /= undefined,

         >>> if Registered -> unregister(Name); true -> nil end.


Thanks you very much for looking at this!

Henning


More information about the erlang-questions mailing list