Use of logical "not" in `case`

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Use of logical "not" in `case`

zxq9-2
Someone asked me an interesting question about readability of case statements, and I'd like some alternative opinions (because I can't decide myself).

When writing a case statement based on a boolean expression, like whether or not to take an action depending on whether or not a given value is a key in a map, I tend to put the value that causes something to happen first assuming that this will be the most interesting path.

For example:


foo(Status, Thingy, Assignments) ->
    case maps:is_key(Thingy, Assignment) of
        false ->
            Assignment = fomulate_assignment(Thingy),
            NewAssignments = maps:put(Thingy, Assignment, Assignments),
            {assigned, NewAssignments};
        true ->
            {Status, Assignments}
    end.


This isn't the best example case, but you get the idea. We care about the case where Thingy was not already assigned and have to do something there, and we just pass the values back in the case it already was. Here the false case came first -- it could have come second, that doesn't *really* matter to me, but I tend to put the interesting stuff up front.

Now compare:


foo(Status, Thingy, Assignments) ->
    case not maps:is_key(Thingy, Assignment) of
        true ->
            Assignment = fomulate_assignment(Thingy),
            NewAssignments = maps:put(Thingy, Assignment, Assignments),
            {assigned, NewAssignments};
        false ->
            {Status, Assignments}
    end.


The argument was that 'true' should always be the more interesting case (whenever there is a more interesting case) because that reads closer to the semantics of the check being done were we to describe it in conversation "If A is *not* a member of B, then [stuff]".

That makes sense to me. It sounds like a reasonable argument.

This isn't a big deal either way of course (I find them both equally readable), but I am curious if anyone has a strong opinion on the issue and why. Which would you prefer to encounter when reading a project to understand how it works on github?

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Use of logical "not" in `case`

Loïc Hoguin-3
On 02/09/2018 10:31 AM, [hidden email] wrote:
[...]
> foo(Status, Thingy, Assignments) ->
>      case not maps:is_key(Thingy, Assignment) of
>          true ->
>              Assignment = fomulate_assignment(Thingy),
>              NewAssignments = maps:put(Thingy, Assignment, Assignments),
>              {assigned, NewAssignments};
>          false ->
>              {Status, Assignments}
>      end.
[...]
> This isn't a big deal either way of course (I find them both equally readable), but I am curious if anyone has a strong opinion on the issue and why. Which would you prefer to encounter when reading a project to understand how it works on github?

Oh boy. I would definitely get tripped by this. Or at the very least
have some eyebrow action.

I have no problem with it in something like C because there's usually no
'else' clause in these cases and it's therefore very easy to read the
intent.

But in Erlang's case what you effectively do is invert the boolean value
below and that requires a little more gymnastic. And I don't think it
matches what one would say in a conversation because why would you
mention the case where nothing happens at all?

--
Loïc Hoguin
https://ninenines.eu
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Use of logical "not" in `case`

Michael Nisi
In reply to this post by zxq9-2
The `not maps:is_key` makes it NOT less confusing.

`true` or `false` don’t carry meaning per se, thus cannot be used to form an argument.
The question gives meaning.

Are you hungry?
Are you not hungry?

Simpler questions are better.

Michael

> On Feb 9, 2018, at 10:31 AM, [hidden email] wrote:
>
> foo(Status, Thingy, Assignments) ->
>    case not maps:is_key(Thingy, Assignment) of
>        true ->
>            Assignment = fomulate_assignment(Thingy),
>            NewAssignments = maps:put(Thingy, Assignment, Assignments),
>            {assigned, NewAssignments};
>        false ->
>            {Status, Assignments}
>    end.

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Use of logical "not" in `case`

Mikael Pettersson-5
In reply to this post by Loïc Hoguin-3
I do prefer the "true" case first, since that more closely emulates a
traditional "if" statement.  However, the condition should then be
written to match.  In your case, I'd prefer a helper function
is_key_absent/2 (or sth like that) rather than a barely visible "not".

Just my 0.02 SEK.

On Fri, Feb 9, 2018 at 10:40 AM, Loïc Hoguin <[hidden email]> wrote:

> On 02/09/2018 10:31 AM, [hidden email] wrote:
> [...]
>>
>> foo(Status, Thingy, Assignments) ->
>>      case not maps:is_key(Thingy, Assignment) of
>>          true ->
>>              Assignment = fomulate_assignment(Thingy),
>>              NewAssignments = maps:put(Thingy, Assignment, Assignments),
>>              {assigned, NewAssignments};
>>          false ->
>>              {Status, Assignments}
>>      end.
>
> [...]
>>
>> This isn't a big deal either way of course (I find them both equally
>> readable), but I am curious if anyone has a strong opinion on the issue and
>> why. Which would you prefer to encounter when reading a project to
>> understand how it works on github?
>
>
> Oh boy. I would definitely get tripped by this. Or at the very least have
> some eyebrow action.
>
> I have no problem with it in something like C because there's usually no
> 'else' clause in these cases and it's therefore very easy to read the
> intent.
>
> But in Erlang's case what you effectively do is invert the boolean value
> below and that requires a little more gymnastic. And I don't think it
> matches what one would say in a conversation because why would you mention
> the case where nothing happens at all?
>
> --
> Loïc Hoguin
> https://ninenines.eu
>
> _______________________________________________
> erlang-questions mailing list
> [hidden email]
> http://erlang.org/mailman/listinfo/erlang-questions
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Use of logical "not" in `case`

Robert Carbone
In reply to this post by Loïc Hoguin-3

I am with Loïc, I'd be tripped—

A flurry of thoughts · · ·

    "When writing a case statement based on a boolean expression"
· As we're only working with a boolean(), why use the case at all?
   That may be where the confusion is coming form.
   The big thing happening here is the true or false, I believe using variables makes that clearer.

     foo(Status, Thingy, Assignments)
       ->
                     TF = <a class="moz-txt-link-freetext" href="maps:is_key(Thingy">maps:is_key(Thingy, Assignments),
                        if
           TF =:= false -> {Status, Assignments}                                          
         ; TF =:= true  ->          Assignment  = fomulate_assignment(Thingy),
                                 NewAssignments = <a class="moz-txt-link-freetext" href="maps:put(Thingy">maps:put(Thingy, Assignment, Assignments),
                      {assigned, NewAssignments}
                      end.

· I tend to put the shorter clause one near the top(see above), so the reader can get take a breath quicker.

· I am have no problem, mentally, saying "otherwise" when I see a  "; true ->" because of the way Erlang treats 'if' expressions. So it can come second just as easily.

· The other day I read that the brain can handle complex data types way better than it candle handle complex logic... I guess anything to does not add to the confusion, not unlike the 'not', isn't that terrible. ;)

· What Dave Thomas said at last year's conference may apply here:

        Programming is Nested Composable Transformation
        Rules should be:
           1. Work out what should be true ->                    
           2. Make it true  ( establishing initial conditions ) 
           3. Keep it true   ( during transformations ) 
                  Also, mentioned,
               · Ask yourself, how does the language encourage you to think about things?
               · If you make it easier to think about things in terms of reducers,
                 the quality of programming will increase.

Cheers,
Rob Carbone
scriptculture.com



On 2/9/18 4:40 AM, Loïc Hoguin wrote:
On 02/09/2018 10:31 AM, [hidden email] wrote:
[...]
foo(Status, Thingy, Assignments) ->
     case not <a class="moz-txt-link-freetext" href="maps:is_key(Thingy">maps:is_key(Thingy, Assignment) of
         true ->
             Assignment = fomulate_assignment(Thingy),
             NewAssignments = <a class="moz-txt-link-freetext" href="maps:put(Thingy">maps:put(Thingy, Assignment, Assignments),
             {assigned, NewAssignments};
         false ->
             {Status, Assignments}
     end.
[...]
This isn't a big deal either way of course (I find them both equally readable), but I am curious if anyone has a strong opinion on the issue and why. Which would you prefer to encounter when reading a project to understand how it works on github?

Oh boy. I would definitely get tripped by this. Or at the very least have some eyebrow action.

I have no problem with it in something like C because there's usually no 'else' clause in these cases and it's therefore very easy to read the intent.

But in Erlang's case what you effectively do is invert the boolean value below and that requires a little more gymnastic. And I don't think it matches what one would say in a conversation because why would you mention the case where nothing happens at all?



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Use of logical "not" in `case`

zxq9-2
In reply to this post by zxq9-2
On 2018年2月12日月曜日 0時48分56秒 JST Richard O'Keefe wrote:

> (a) I don't care whether false precedes true or true precedes false.
> (b) I *definitely* get confused by 'not'.  Human beings in general
>     are stunningly bad with 'not'.  Empirical studies have shown
>     that programmers are not good with it either.
> (c) I have learned to be wary of 'boolean'.  Long experience teaches
>     me that either there are more than 2 possibilities, or that there
>     is more to say one at least one case, and often both.  Here I
>     would erase from my memory all trivial fond memories of maps:is_key/2
>     and write
>         case maps:find(Thingy, Map)
>           of {ok, _Old_Value} -> ...
>            ; error            -> ...
>         end
>     so that there was no doubt about which branch was which.
>     Seriously: your grandfather had to use IF and COND a lot when he
>     wrote Lisp, but *we* have pattern matching.
> (d) In fact, we really do have pattern matching, so here
>         case Map
>           of #{Thingy := _} -> ...
>            ; _              -> ...
>         end
>     perfectly expresses our intent.


Indeed! The sentiment seems universal.

Thanks to everyone who took the time to explain their opinion on it.
Eventually I'll probably compile a style guide from the various cases
of "is this annoying or not" sort of style questions I've asked here
over the last several years.

Eventually.

- Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions