Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

?: = Obfuscation?

by Melly (Chaplain)
on Dec 01, 2006 at 16:19 UTC ( [id://587227]=perlmeditation: print w/replies, xml ) Need Help??

A couple and half of monks (I'm the half ;) think that the ?: operator-pair is always less readable than an if/else block.

As indicated, I half-agree, but I wonder if this is more a case of "I don't use or am not familiar with ?:, and therefore it's obfuscated" or "I understand the syntax of ?: perfectly, but I still think it makes code hard to read and/or maintain".

The maintainability issue is a tricky one, but, given that ?: is not an operator-pair limited to perl, is it unreasonable to expect any code-maintainer to understand it? (or at least make the effort to find out about it?)

What's the consensus on this one?

map{$a=1-$_/10;map{$d=$a;$e=$b=$_/20-2;map{($d,$e)=(2*$d*$e+$a,$e**2 -$d**2+$b);$c=$d**2+$e**2>4?$d=8:_}1..50;print$c}0..59;print$/}0..20
Tom Melly, pm@tomandlu.co.uk

Replies are listed 'Best First'.
Re: ?: = Obfuscation?
by TimToady (Parson) on Dec 01, 2006 at 17:42 UTC

    If you want consensus on this one, don't ask more than one language designer. :-)

    From a language design point of view, there are certainly some problems with ?:, some of which have been pointed out here. Our approach in Perl 6 has been to try to fix the problems without throwing out the whole idea. One of the problems is simply that it's too hard to see the bits of ?:, so we made the bits bigger. In Perl 6 you use ??!! instead. This also plays into the Perl 6 principle that ? and ! are generally preferred to ask whether things are true or false. There is no natural association between : and the concept of falseness.

    But what is the basic idea of ?: anyway? I think it's there to avoid doing extra assignments. You'll notice all the workarounds involve repeating your destination variable, which violates the "don't repeat yourself" principle, aka the DRY principle. So if that's the basic purpose of ?:, and if we teach people that that's the basic purpose of ?:, then people won't be tempted to write things like:

    $huh ? $x = 42 : $x = 43;
    So education is part of it. But the language can do a little more work for you here, and in fact, if in Perl 6 you say
    $huh ?? $x = 42 !! $x = 43;
    it will give you a syntax error (though pugs doesn't do this correctly yet). What's really going on here is that the Perl 5 compiler is ignoring the precedence of ?: in the middle, as if you had written:
    $huh ? ( $x = 42 ) : $x = 43;

    Perl 6 will not supply the implicit parens there, so if you use any operator with a precedence looser than ??!! in the middle, you'll get a missing !! error. This will catch 90% of the bogus uses of the ternary operator, and help teach people that it doesn't mix well with embedded assignment. It doesn't help with a bogus assignment on the right, of course, but usually that error is caught by the fact that most ternary operators do not return a legal lvalue.

    Any existing language will give you lots of features in a take-it-or-leave-it way, and Perl 5 is no exception. So it's tempting to try to come up with a simple answer to the question of whether a particular feature is good or evil. Often the answer is "It depends." And when that's the answer, it is often an indication that the feature could benefit from a redesign. When you start thinking about it that way, you start to find you can't just think about the one feature in isolation. You start to think that maybe this applies at all different scales to many of the features in Perl 5, and that the fix for one feature often involves fixing a different feature in parallel. And that's the short answer to why we are working on Perl 6.

      If you want consensus on this one, don't ask more than one language designer.
      And even asking one is risky.

      Interesting - and the Perl 6 changes make sense (although they lengthen the golf-courses for everyone ;).

      I noticed the point about the implicit parens between ? and : when I was writing a related tutorial, but just sort of shrugged it off - it seemed reasonable (since '?' implies a closing ':'), but in some ways confusing (since you are more likely to be confused by the behaviour of $x?$y=1:$y=0).

      I intend to revise and improve the tutorial to try and reflect some of the views expressed in this thread, but without swamping the users for whom the tutorial is written with too many distractions.

      BTW do you think, with acknowledgements, it's ok to use code from this thread in the tutorial, or is it polite to ask the individual contributors first?

      map{$a=1-$_/10;map{$d=$a;$e=$b=$_/20-2;map{($d,$e)=(2*$d*$e+$a,$e**2 -$d**2+$b);$c=$d**2+$e**2>4?$d=8:_}1..50;print$c}0..59;print$/}0..20
      Tom Melly, pm@tomandlu.co.uk
        I like ?: when the args are short.

        I spent years as a LISP programmer. A programmer who has memorized the precedence table will write code that's understandable by others who have memorized the precedence table. Use parenthesis liberally.

        $longways = $widthIsLonger ? $width || 1 : $height || 1;
        may work and is terse, but
        $longways = $widthIsLonger ? ($width || 1) : ($height || 1);
        reads better to me.

        It's mostly the case that if I'm using an operator within the scope of ?: I'll parenthesize.

      From a language design point of view, there are certainly some problems with ?:, some of which have been pointed out here. Our approach in Perl 6 has been to try to fix the problems without throwing out the whole idea. One of the problems is simply that it's too hard to see the bits of ?:, so we made the bits bigger. In Perl 6 you use ??!! instead. This also plays into the Perl 6 principle that ? and ! are generally preferred to ask whether things are true or false. There is no natural association between : and the concept of falseness.

      Speaking of the ternary operator (whatever it' spelt), I've sometimes felt the need to make it... ehm... quaternary. Specifically, in cases in which I wanted it to return something different on true, false (but defined) and undefined first argument respectively. Now, before pointing out that there are ways around, let me tell you that I know there are tons in Perl 5 and presumably even more so in Perl 6. But I'd also like a syntactically sweet enough solution out of the box, and I wondered whether an optional <c<!!</c> may trigger that:

      my $wanted = $cond ?? $true !! $false !! $undefined;
Re: ?: = Obfuscation? (value)
by tye (Sage) on Dec 01, 2006 at 17:25 UTC

    ?: gives back a value. Using it in a situation where you don't use the return value is a good indication that you should probably just replace it with if/else. Although Perl allows if/else to give back a value as well, using it in that way is usually a good way to surprise people.

    So I use ?: only for fairly simple cases of just picking between two values (or perhaps a few more, via multiple uses of ?:). I use if/else if the primary point is not simply giving back either this or that value. For cases of picking between some values that aren't quite simple, I use sub. Of course, using sub for a simple case of picking between two values is also acceptable and is sometimes a much better choice than inlining a ?:.

    Note that using ?: as it was intended (to return a value) also usually means that you don't run into precedence problems and so you usually won't need parens (which can quickly become visual clutter if many are required).

    And, using if/else is often appropriate or even better than ?:. For example, in:

    $x= cond() ? this() : that(); # vs. if( cond() ) { $x= this(); } else { $x= that(); }

    The more verbose version might be preferred just for style reasons, because it is easier to add comments to without impacting readability, or because it is suspected that future maintenance will complicate the two blocks to the point of making ?: inappropriate anyway.

    Of course, the ?: has some advantages that can be important sometimes. Obviously, it is more compact. Although making code too compact can make it harder to read, I also find that making code too expansive can make it hard to read. That can distract the viewer by giving too much space to items of relatively little importance or it can make it impossible to view an entire logical structure and get the feel for the "big picture" of it.

    Also, $x is not repeated in the compact form. This becomes even more of an advantage in a fairly common example like this:

    my $x= cond() ? this() : that(); # vs. my $x; if( cond() ) { $x= this(); } else { $x= that(); }

    Note that we had to repeat $x three times for the sake of using if/else.

    And you might have good reason to prefer to use if/else as part of a way to simply return either this or that value:

    $x= cond() ? this() : that(); # vs. sub pick { if( cond() ) { return this(); } else { return that(); } } $x= pick();

    The biggest advantage of ?: is for cases like:

    my $x= DoSomething( cond() ? this() : that(), $whatever, foo() ? bar() : baz(), $blah, );

    Finally, there are rare cases where restrictions make using the rather surprising ability of if/else to give back a value quite appropriate. For example:

    s{ # Some fairly complex parsing regex }{ my( ... )= ( $1, $2, $3, $4 ); if( ... ) { ... "replacementString"; } elsif( ... ) { ... "differentReplacement"; } else { "somethingElse"; } }gex;

    - tye        

Re: ?: = Obfuscation?
by philcrow (Priest) on Dec 01, 2006 at 16:48 UTC
    If the test is really short, you can make tables with either if/else or ?:
    if ( $score >= 90 ) { $grade = 'A'; } elsif ( $score >= 80 ) { $grade = 'B'; } #... else { $grade = 'F'; }
    $grade = ( $score >= 90 ) ? 'A' : ( $score >= 80 ) ? 'B' : ( $score >= 70 ) ? 'C' : ( $score >= 60 ) ? 'D' : 'F';
    Both of these degrade badly as the line length grows.

    But, even if you like ifs (which I do mostly), tables are better than:

    if ( $score >= 90 ) { $grade = 'A'; } elsif ( $score >= 80 ) { $grade = 'B'; } #...

    Phil

Re: ?: = Obfuscation?
by perrin (Chancellor) on Dec 01, 2006 at 17:25 UTC
    I only use it in places where it's impossible to use an if/else without breaking things up too much. For example, in a hash assignment:
    %hash = ( a => 1, b => ($c ? 'yes' : 'no'), );
      only use it in places where it's impossible to use an if/else without breaking things up too much. For example, in a hash assignment:
      %hash = ( a => 1, b => ($c ? 'yes' : 'no'), );
      seconded

      It's not what you look like, when you're doin' what you’re doin'.
      It's what you’re doin' when you’re doin' what you look like you’re doin'!
           - Charles Wright & the Watts 103rd Street Rhythm Band, Express yourself
Re: ?: = Obfuscation?
by imp (Priest) on Dec 01, 2006 at 16:30 UTC
    Like most things in perl the ternary operator can both help and hinder readability.

    I prefer ternary for this:

    # With ternary my $x = exists $hash{key} ? $hash{key} : 'default' # Without my $x; if (exists $hash{key}) { $x = $hash{key}; } else { $x = 'default'; }
    I'm on the fence for this:
    # With ternary my $x = $x > 5 ? 5 : $x + 1; # With if if ($x > 5) { $x = 5; } else { $x++; }
    And dead set against this:
    my $x = $y > 5 ? ($y = 0) : ($y = 1);

      And dead set against this:

      my $x = $y > 5 ? ($y = 0) : ($y = 1);

      As you should be.

      my $x = ($y = $y > 5 ? 0 : 1);

      and

      $y = $y > 5 ? 0 : 1; my $x = $y;

      are much more readable.

Re: ?: = Obfuscation?
by brig (Scribe) on Dec 01, 2006 at 22:44 UTC

    Terse Good, Obfuscated Bad

    I have been reading Perl Best Practices by thedamian. When I first read what he had to say about using ?: I was only half listening (if that makes sense ;) - but I was struck by how he laid out the following code. At first I didn't like how it was spread out but as I traced the logic I realized it was beautiful.

    # When their name is... Address them as... my $salute = $name eq $EMPTY_STR ? 'Customer' : $name =~ m/\A((?:Sir|Dame) \s+ \S+) /xms ? $1 : $name =~ m/(.*), \s+ Ph[.]?D \z /xms ? "Dr $1" : $name ;
    Code From: Perl Best Practices, Damian Conway PBP @ O'Reilly

    My big gripe has already been addressed above, ?: has to be used in the correct circumstances. The way I generally use it is usually as a one line choice of functions:

    $conditional ? foo() : bar();

    or

    $conditional ? foo($arg_1) : foo($arg_2);

    What I don't like to see is extra processing in the choices:

    $conditional ? $foo++ : $foo += 2;

    When there are assignments involved it can get confusing and I would agree that a regular if else block is more appropriate and more easily expanded with extra logic.

    ??|| IMHO (I'm sorry) but that is a messy solution to a non language problem. The problem is not that the ?: is hard to see, it is that it gets abused... Kinda like forcing all cars to have square wheels to keep drunks from driving.

    I hope this 2¢ helps :)
    --Brig

    Update: Added link to Damian's node.

    Update: The reason I am tolerant of the "extra processing" in the first example is that the processing is a string search. When the search hits, the code directly to the right executes. Therefore I believe this is a very readable construct. The only drawback is that the regex must be reasonably short, I am not strict on the 80 column rule but it is a good rule.

      Actually I don´t like much the ternary operator, except when it's shorter and readable, and maybe a few C-like uses...

      and I was really surprised by PBP on this; I had thought ?: would be simply rejected, and comes that incredible table layout, really amazing one of the gems of the book

      still only half-way convinced ;) I don´t know why...a gut feeling mainly... funny we thought more or less the same way

      hth --stephan
Re: ?: = Obfuscation?
by chargrill (Parson) on Dec 01, 2006 at 16:34 UTC

    I think the ternary operator is perfectly fine when used appropriately. For example, the following snippet:

    print $_ ? do { $trip = $trip ? 0 : 1; $_ } && $_ : $trip ? !( $counter % 4 ) ? do { $nexchar = pop @string +_arr; '^' eq $nexchar ? ' ' : $nex +char } || '-' : ' ' : ' ';

    ... is a fine example of how NOT to use it when attempting to write maintainable code. Indeed, that's pulled from a properly indented and slightly deobfuscated obfuscation. On the other hand,

    my $x = $counter > 4 ? 1 : 0;

    ... seems like an acceptable alternative to the more verbose:

    my $x; if( $counter > 4 ){ $x = 1; } else { $x = 0; }

    However, this type of shorthand is often best left to be decided by your environment. If you find yourself amidst folk who are comfortable with shorthand, by all means use that as a coding standard: "Where readability is not hindered, the use of the ternary operator in assignments is encouraged". If on the other hand your environment dictates that code be more explicit, whether its due to strict maintainability requirements or the presense of junior developers as maintainers, then adjust your coding standards to suit.



    --chargrill
    s**lil*; $*=join'',sort split q**; s;.*;grr; &&s+(.(.)).+$2$1+; $; = qq-$_-;s,.*,ahc,;$,.=chop for split q,,,reverse;print for($,,$;,$*,$/)
Re: ?: = Obfuscation?
by Tanktalus (Canon) on Dec 01, 2006 at 19:30 UTC

    I think you're tallying me in that "couple and half" so I'd like to clarify. I didn't say the ?: operator is always less readable than an if/else block. I only meant that the ?: operator is less readable than an if/else block when you're doing two different things, especially to two different variables. $y = $x ? 1 : 0 is fine. But if you're trying to set one of $y or $z based on $x, well, just saying that is complicated. The code should always reflect the (human) thought process that it is describing - a little extra verbosity in the code to reflect extra spoken verbosity is a good thing. (Please don't take that to extremes. I'm not advocating useless extra code here.)

    As far as understanding it - yes, I expect my junior coworkers to understand it. Just not abuse it. Perl, like English, has a natural flow to it. If you try to say something with an unnatural grammar, people may understand it even though it may jar their ears to hear it. Similarly, perl's natural flow can be disrupted and the code still works.

    If your code follows the natural flow of the language, the listener (maintainer) who comes along will be able to just read your code. When you do tricky things, you disrupt the maintainer's flow, and they'll need to do a double-take to figure out what really is going on there. If that isn't the problem area, they just wasted time deciphering something that turned out to be irrelevant. And that just annoys us maintainers ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://587227]
Approved by chargrill
Front-paged by tye
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (9)
As of 2024-04-19 06:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found