Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Ternary operators: a hinderance, not a help

by Tanalis (Curate)
on Aug 09, 2005 at 15:16 UTC ( [id://482240]=perlmeditation: print w/replies, xml ) Need Help??

It's been a long while since I've felt inspired or passionate enough about something to write a meditation about it.

I found myself in a situation this morning where I needed to assign a number or zero to a variable depending whether another variable was set or not.

Now, my normal reaction to this would be to use an if to write something like this:

my $number; if( $logical_test ) { $number = $value; } else { $number = 0; }
.. and then I remembered the ternary operator existed, and that I could save myself code, and some keystrokes, by using
my $number = $logical_test ? $value : 0;
instead. It was early, and my brain saw this as a reasonable option, probably due to lack of coffee.

Now, in this contrived example, it's pretty obvious to see what's going on and what's being assigned where. Consider, however, extending this to a more complex real-world scenario, involving accessing elements in deep data structures, subroutines to format numbers, regular expressions and the like. Clarity, maintainability and even code elegance are quickly lost, all for the sake of a few keystrokes and a newline, unless the person doing the coding is very careful about how he uses the operator.

Where the use of the if makes it obvious what the value of the variable is being set to, I found that even a little while later, the meaning of the ternary operator was significantly less clear. I tried a few other examples, and found that the same assertion held for all of them: the ternery operator hinders, not helps, the understanding of code.

Turning to my now-trusty copy of Perl Best Practises, hoping to find some rule to make this a lot clearer, TheDamian's suggestion is to format cascaded ternery operators in columns(1). I agree that this helps to undo some of the damage done to the code's clarity, but I still think there's a way to go. If I'm honest, I don't think that I completely buy TheDamian's explainations as to why the ternary operator is advantageous over an if construct - sacrifices seem to be made in both options - and I'd rather sacrifice compactness over maintainability.

I talked with a couple of colleagues about this, and found that opinion seems to be divided one way or the other: either you love boolean ? value_if_true : value_if_false, or you hate it.

Why choose one over another? I guess it's another one of those situations that come down to personal preference. There's more than one way to do it, after all.

What do others think? Is this simply a preference issue, or am I missing something?

Update: Changed if example to reflect Joost's feedback.

Replies are listed 'Best First'.
Re: Ternary operators: a hinderance, not a help
by revdiablo (Prior) on Aug 09, 2005 at 15:33 UTC

    My opinion of the ternary has changed throughout the years. I don't really know why, but it has. It might change again, so don't hold me accountable for this in the future. :-) For a long time, I was on your side of the camp. I really didn't like it at all. I thought it was ugly and pointless.

    Nowadays I have swung back to the other side. As long as it is used for assignment, and not for side-effects, I don't see a problem with it. In fact, in some circumstances I think it's quite nice. It handles the assignment in one semantic unit (i.e. a single statement), rather than having the default value and the possible new value split up among two statements. Note, nowhere did I mention anything about saving a line of code. I'm all abount semantics, baby. Usually I even spread the ternary out into 3 lines of code, which I find more easily readable:

    my $variable = $test ? $value : 0;
Re: Ternary operators: a hinderance, not a help
by GrandFather (Saint) on Aug 09, 2005 at 22:06 UTC

    It is interesting to note that the trinary operator can go on the LHS of an assignment:

    use warnings; use strict; my $var1 = ""; my $var0 = ""; 1 ? $var1 : $var0 = "Hello "; 0 ? $var1 : $var0 = "World\n"; print $var1.$var0;

    Perl is Huffman encoded by design.
Re: Ternary operators: a hinderance, not a help
by friedo (Prior) on Aug 09, 2005 at 18:20 UTC
    Like everything in Perl, the ternary is ripe for abuse if you want to abuse it. I used to work with a guy who wrote stuff like this:

    my $send_now_button = ( !$auth{SEND} ? '' : $status != 1 ? '' : $approved ? $send_now ? '' : qq( ...blah blah ... ) : $send_now ? '' : '' );

    Yes, that's a real-life example, in production code, for a multi-million dollar corporation, although I cleaned it up a bit.

    Personally, I like the ternary, as long as you don't go nuts with it. I only use it in assignments, and I don't nest them.

      I expect that the corresponding nested if..else construct would be even more baffling. The weakness here is in the negated and chopped-up logic, not the trinary op. I'll make a first cut at rewriting it:

      my $send_now_button = $auth{'SEND'} && $status==1 && $approved && !$send_now ? qq( ...blah blah ...) : '';
      Not nested, you can use it now ;-)

      I think I had an easier time decyphering the (well-formatted!) nested trinary than I would have a similarly built if statement.

      Update: Just for fun, here's thing I'm slanging, formatted the best I know how,

      my $send_now_button; if ( !$auth{'SEND'} ) { $send_now_button = ''; } else { if ( $status != 1 ) { $send_now_button = ''; } else { if ( $approved ) { if ( $send_now ) { $send_now_button = ''; } else { $send_now_button = qq( ...blah blah ...); } } else { if ( $send_now ) { $send_now_button = ''; } else { $send_now_button = ''; } } } }
      Noisy, ain't it? That maybe does even better than the trinary version at pointing out how ludicrous the logic is, but IMO trinary makes it easier to see how to fix it. I didn't even bring elsif to the table :-))

      After Compline,
      Zaxo

      Isn't it fun to know people like that? Such a trip!

      I wouldn't mind "wasting" a line on a default assignment in a case like that (and I often do it this way):

      my $send_now_button = ''; # that's the default value; $send_now_button = qq( ...blah blah ... ) if ( $auth{SEND} and status == 1 and $approved and ! $send_now );
Re: Ternary operators: a hinderance, not a help
by phaylon (Curate) on Aug 09, 2005 at 15:34 UTC
    I just use it when I find it is more readable. Sometimes I have things like
    my $var = ( $EXPR ? sprintf( '...', $x ) : sprintf( '...', $y ) );

    which I find more readable than a
    my $var; if ( $EXPR ) { $var = sprintf( '...', $x ); } else { $var = sprintf( '...', $y ); }

    I think it gives the possibility to keep together what should stand together. I also often found, that those real life scenarios you speak of are simpler to solve with the ternary operator, than with fully written conditionals.

    But in the end, it's surely a question of personal preference :)

    Ordinary morality is for ordinary people. -- Aleister Crowley
      For that matter, if the "..." is the same in your two conditions, I'd do it like this:
      my $val = ( $EXPR ) ? $x : $y; my $var = sprintf( "...", $val );
      OTOH, if the "..." is different in each case, I might take the trouble to put that into a hash...
      my %format = ( $x => "... for x", $y => "... for y" ); my $val = ( $EXPR ) ? $x : $y; my $var = sprintf( $format{$val}, $val );
      And I agree with you and with earlier replies: it's a matter of personal preference for those of us who feel a sense of "semantic unity" in certain conditional assignments.
Re: Ternary operators: a hinderance, not a help
by eric256 (Parson) on Aug 09, 2005 at 19:14 UTC

    I stayed away from ternary for a long time, but once you get use to it, it is a nice tool to have. It is certainly not a choice of either ternary or if, keep both in your tool box and you will be happy. I use ternary when they are simple cases like your example. It is ofter the most usefull for defaulting values and not creating several lines of if constructs. It is also usefull if you want to embed different things in a string.

    print "You have $items " . ($items == 1 ? "object." : "objects.");

    Certianly not the most poetic use of it, but it should demonstrate the use well enough. If you find your self using temporary variable and if structures, it might just be that you can use a tenrary to replace them. Now in that same example if i wanted to say "object(s)" more than once you can break it out int a single ternary:

    my $objects = $items == 1 ? "object" : "objects"; print "You have $items $objects. What would you like to do with your $ +objects?";

    And then if you I am going to change multiple variable based on the state of one, use an if. These are just my rules, guidlines, expereinces. Keep all your tools handy and sharp, because most things in perl fit somewhere, honest, they weren't just thrown in for the heck of it. Like any tool, if used wrong, they can do more damage than good. ;)


    ___________
    Eric Hodges
Re: Ternary operators: a hinderance, not a help
by Joost (Canon) on Aug 09, 2005 at 15:40 UTC
    update: the indented block is irrelevant now, with the update in the OP
    Your examples are not equivalent. Your "if" case should look something like this:
    my $number = do { if ($logical_test) { $value } else { 0 } }

    Note that there is no automatic assignment of the 0 value. Doesn't make much difference here, ofcourse, but for more complicated cases its possible that those assignments have side-effects, or just take a lot of time.

    Anyway, I would not recommend using ?: in very complex statements, because the ? and : tend to get drowned. But then, I would not recommend using statement modifiers in such a case either:

    $bla = $some_really_long_calculation_that / $you_just_don_t * $want_to +_read_all_the_way_to_the_end if $something;

      I .. wouldn't have written the if that way, either, though .. :)

      I've changed the example in the OP to make it clearer - thanks :)

Re: Ternary operators: a hinderance, not a help
by spiritway (Vicar) on Aug 09, 2005 at 18:42 UTC

    I think the ternary operator is just fine for simple tasks. It gets confusing, though, when someone shows their coding machismo by doing something like:

     $number= $test ? $value : ($something_else ? $foo : $bar)...

    Back in the days of dumb compilers it made some sense to write things like that, on the theory that the compiler's output would be more efficient. You had to show it how to optimize. Now it's not necessary. We're writing code for humans to read, not for compilers. Clarity is (IMNSHO) more important than efficiency, now that compilers generally are capable of outputting efficient machine code.

Re: Ternary operators: a hinderance, not a help
by xdg (Monsignor) on Aug 09, 2005 at 16:32 UTC

    If it's used to enhance readability, I'm in favor. Look at your example -- 7 lines of code vs 1 line of code. As long as the tests and predicates are short, single line constructs, I think the vertical space savings of the ternary operator are worthwhile, even when chained.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Agreed, for that example.

      As I state in the OP, however, my point about readability and clarity is concerned more with more complex constructs, of which that example is not one :)

      Vertical space savings are all well and good, but not at the expense of readability. Chained/cascading ternaries quickly lose readability, especially when combined with other code elements.

        I think that what you may be missing is that vertical space savings often enhance readability in terms of making it possible to see the whole forest at once.

        In the same vein, I've seen (mostly beginning C++) people say that printf-formatted strings are too much like line noise, and everything should really be formatted through the expressive cout format, with operators to change the number of digits of precision, etc.

        The same people might well complain about pack. The common lisp community often fields complaints about how terribly obscure the syntax is to format.

        The point is that compressing stuff that's not terribly important to understanding the whole improves understanding of the code overall. Good code should read like stuff you want to read, and my eyes glaze over with too much pointless verbosity. (Usually, my eyes glazing is a sign that the code I'm reading suffers from severe cut-and-paste and really needs to be refactored to deal with multiple cases in a more generic fashion.)

        You've obviously encountered several bad uses of the ?: operator. Allow me to present at least one good one:

        my $numspells = ($doDeanStuff ? 1 : 0) + ($doSeamusStuff ? 1 : 0) + ($doHarryStuff ? 2 : 0) # Harry's stuff takes multipl +e spells + ($doRonStuff ? 1 : 0) + ($doSeverusStuff ? 5 : 0); # it's really complicated print "Budget for this incantation is $numspells spells\n";
        Now in this case I'm sure you could do:
        my $numspells = 0; $numspells += 1 if ($doDeanStuff); $numspells += 1 if ($doSeamusStuff); $numspells += 2 if ($doHarryStuff); # Harry's stuff takes multiple sp +ells $numspells += 1 if ($doRonStuff); $numspells += 5 if ($doSeverusStuff); # it's really complicated
        But I think that the first version is easier - or at least, faster - to read. (more whitespace per bit of meaning) And code that's faster to read is more likely to get read in full before someone makes changes to it.

        (Yeah, I'm just getting around to reading book 6 now)

        -- @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/
        As I state in the OP, however, my point about readability and clarity is concerned more with more complex constructs, of which that example is not one :)
        I got the impression the OP condemned all use of ?:. If your opinion is "use ?: when that's readable, use if/then/else when that is", you and I agree, because that's what I do. I use ?: when I find that more readable if/then/else, otherwise, I use if/then/else.

        Chained/cascading ternaries quickly lose readability, especially when combined with other code elements.
        So do chained/cascading if/then/else constructs. Or nested loops, or nested indices.

        Your original example, I would write either as:

        my $number = $logical_test ? $value : 0;
        or
        my $number = $value; $number = 0 unless $logical_test;
        or
        my $number = 0; $number = $value if $logical_test;
        depending whether I want to stress the default value of $number. (I might use one of the latter two if $logical_test is expected to have a particular value, and it not having it is an exception, I use the former line if $logical_test could go both ways).
        If $logical_test is 0 if it's false, I might even write it as:
        my $number = $logical_test && $value;

        I wouldn't easily write:

        my $number; if ($logical_test) { $number = $value; } else { $number = 0; }
        as it assigns an initial value to $number in a different scope than its declaration - IMO, that doesn't score bonus points when it comes to readability. It's also 7 lines instead of 1, although it can easily be shortened to:
        my $number; if ($logical_test) {$number = $value} else {$number = 0}
        But still, it's three lines. And worse, both blocks have two thirds of their tokens in common - which just shouts "factor out, factor out". Which would lead to:
        my $number = do {if ($logical_test) {$value} else {0}};
        But then I prefer:
        my $number = $logical_test ? $value : 0;
Re: Ternary operators: a hinderance, not a help
by BitDreamer (Sexton) on Mar 14, 2018 at 18:51 UTC
    This is an old thread, but I can't believe nobody used this kind of example:
    $statement_type = $hate_ternary ? "if (condition) {} else {}" : $love_ternary ? 'var = condition ? valuetrue : condition2 ? valuefalsetrue : valuefalse' : 'keep = it ? simple : stupid';
    

    Here are my personal guidelines:
    Rule 1 - keep ternary operations simple
    Rule 2 - use the best format or method
    Rule 3 - if you really have to combine tenaries, it's better to daisychain them than to nest them (hint - complicate the False term, not the True term)

    Combining ternaries
    Not OK:
    my $value = $condition1 ? ($condition2 ? $value1 : $value2) : $value3;
    OK for some programmers:
    my $value = $condition1 ? $value1 : $condition2 ? $value2 : $value3;

    That last example is equivalent to a basic if, elsif, else. This should only be done for basic and simple tasks.

      Personally, I only use ternary ops (which I do use frequently) if it is as basic as it gets:

      return $x ? $y : $z;

      If there is more than one condition, it's if/elsif/else, and if it gets much beyond that, I use a hash/dispatch table or some form of hash mechanism to just "drop-in" the result of the condition to present the result I want.

Re: Ternary operators: a hinderance, not a help
by adrianh (Chancellor) on Aug 10, 2005 at 10:48 UTC
    Now, in this contrived example, it's pretty obvious to see what's going on and what's being assigned where. Consider, however, extending this to a more complex real-world scenario, involving accessing elements in deep data structures, subroutines to format numbers, regular expressions and the like. Clarity, maintainability and even code elegance are quickly lost, all for the sake of a few keystrokes and a newline, unless the person doing the coding is very careful about how he uses the operator.

    Very careful? Is it really that hard to see when if/then/else is going to be less clear than the ternary operator (or vice versa)?

    Use whichever one's the clearest in a particular situation. Saying that one or the other is less clear or maintainable without a particular context is just silly :-)

Re: Ternary operators: a hinderance, not a help
by Koolstylez (Scribe) on Aug 10, 2005 at 17:50 UTC
    I talked with a couple of colleagues about this, and found that opinion seems to be divided one way or the other: either you love boolean ? value_if_true : value_if_false, or you hate it.

    I think it's a bad idea to love an operator. They always end up leaving you for a younger and better looking programmer ;)

    Seriously though, loving or hating an operator isn't a good idea. Most of the constructs a programming language like Perl offers you can be abused to produce nearly unreadable code (see Obfuscation). It's up to the programmer to write maintainable code, and _sometimes_ the ternary operator can make your code more readable.
Re: Ternary operators: a hinderance, not a help
by NateTut (Deacon) on Aug 10, 2005 at 15:35 UTC
    I use them a lot when I build SQL:my $Show_Statement = "Show " . ($row->{'TableKind'} eq 'T' ? 'Table' : 'View') .  $row->{'DatabaseName'} . '.' . $row->{'TableName'}; I find that method more readable than using an if statement.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (4)
As of 2024-04-24 19:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found