• Ziplodocus (unregistered)

    Count(comments) = 0 ? "Frist" : "Discourse is the real WTF"

  • cyborg (unregistered) in reply to Ziplodocus
    Ziplodocus:
    Count(comments) = 0 ? "Frist" : "Discourse is the real WTF"

    An ingenium example of the frist reason to use ternaries.

  • (cs) in reply to Ziplodocus
    Ziplodocus:
    Count(comments) = 0 ? "Frist" : "Discourse is the real WTF"
    Discourse?WTF:Thrid!
  • Smug Unix User (unregistered)

    Stop trying to make discourse happen.

  • CigarDoug (unregistered)

    Personally, in all my years of reading the daily WTF, I have had zero use for Discourse.

  • my name (unregistered)

    dis·course, Latin - from the verb discurrere, from dis- 'away' + currere 'to run'.

    So it originally meant 'run away'? See, even the Ancient Romans were trying to warn us against using it!

  • n/a (unregistered)

    Discourse in an ancient African word meaning "I'll spam your browser history with thousands of meaningless links".

  • (disco)

    I suggest that the string "Frist!" be prepended to @PaulaBean's posts.

  • (disco)

    Well, ok. This code sample clearly shows how bit flags shouldn't be read.

    However, the introduction text is something I vehemently disagree with. The ternary operator is a great tool to make code more readable by dropping unnecessary syntactic verbosity. Not to forget, it gives you a LISP-style if in C-Style languages without any parenthesis hell!

  • (disco)

    Btw, that line of code doesn't seem to be doing anything. It's just an expression that returns a string.

    Are they missing an assigment?

  • (disco)

    I knew someone who wrote a functional program in C with just one function — main() — and just one statement — return. It was some of the most flagrant abuse of ternaries and comma operators that I've ever seen, and that was before you got into the realms of all the undefined behaviour with which it was strewn.

    It edited DOS boot sectors (as far as I can remember; it was a long time ago).

  • (disco)

    The ternary abuse is one thing ... but I cannot figure out the bit operators.

    At some point you don't have a bitfield anymore, you functionally have an integer. This seems like a really bad way to write "switch (FailSafe & 0x0F)"

  • (disco) in reply to peppernut
    peppernut:
    The ternary abuse is one thing ... but I cannot figure out the bit operators.
    I think that for each bit they concatenate strings: bit 0 is 'Detection zones staying in a given state', bit 1 is 'Bad visibility', etc. So the first condition tests for all bits and then returns the string with all error messages. Basically, an index in array of strings would have done the same, but more readable.

    PS Note that it isn't C.

  • (disco)
    the code:
    `...0?'Bad configuration':'Unknown'`
    Of course the original developer of that long chain of ternary operators is doing it wrong!

    ...0?'Bad configuration':'FILE_NOT_FOUND'

    There. Much better!

  • (disco) in reply to aliceif
    aliceif:
    However, the introduction text is something I vehemently disagree with. The ternary operator is a great tool to make code more readable by dropping unnecessary syntactic verbosity.
    I agree with your vehement disagreement. This code would be nearly as WTFy if it was written with `if` statements on one line.

    You could increase readability tremendously just by properly formatting and rewriting the conditions (I'm assuming this is a language with sane precedence and associativity — ie, not PHP — and getting rid of the extraneous parens too):

    ``` FailSafe == 0 ?     'No technical alarms' :      FailSafe & (1|2|4|8) == (1|2|4|8) ?     'Detection zones staying in a given state; Bad visibility; Initialization; Bad configuration' :      FailSafe & (1|2|4) == (1|2|4) ?     'Detection zones staying in a given state; Bad visibility; Initialization' :      FailSafe & (1|2|8) == (1|2|8) ?     'Detection zones staying in a given state; Bad visibility; Bad configuration' :      FailSafe & (1|4|8) == (1|4|8) ?     'Detection zones staying in a given state; Initialization; Bad configuration' :      FailSafe & (2|4|8) == (2|4|8) ?     'Bad visibility; Initialization; Bad configuration' :      FailSafe & (1|2) == (1|2) ?     'Detection zones staying in a given state; Bad visibility' :      FailSafe & (1|4) == (1|4) ?     'Detection zones staying in a given state; Initialization' :      FailSafe & (1|8) == (1|8) ?     'Detection zones staying in a given state; Bad configuration' :      FailSafe & (2|4) == (2|4) ?     'Bad visibility; Initialization' :      FailSafe & (2|8) == (2|8) ?     'Bad visibility; Bad configuration' :      FailSafe & (4|8) == (4|8) ?     'Initialization; Bad configuration' :      FailSafe & 1 == 1 ?     'Detection zones staying in a given state' :      FailSafe & 2 == 2 ?     'Bad visibility' :      FailSafe & 4 == 4 ?     'Initialization' :      FailSafe & 8 == 8 ?     'Bad configuration' :      'Unknown'; ```

    Compare to the same conditions but with if statements instead:

    ``` if (FailSafe == 0){     return 'No technical alarms'; } else if ( (FailSafe & 1) != 0 && (FailSafe & 2) != 0 && (FailSafe & 4) != 0 && (FailSafe & 8) != 0 )}     return 'Detection zones staying in a given state; Bad visibility; Initialization; Bad configuration'; } else if( (FailSafe & 1) != 0 && (FailSafe & 2) != 0 && (FailSafe & 4) != 0 ){     return 'Detection zones staying in a given state; Bad visibility; Initialization'; } else if( (FailSafe & 1) != 0 && (FailSafe & 2) != 0 && (FailSafe & 8) != 0 ){     return 'Detection zones staying in a given state; Bad visibility; Bad configuration'; } else if( (FailSafe & 1) != 0 && (FailSafe & 4) != 0 && (FailSafe & 8) != 0 ){     return 'Detection zones staying in a given state; Initialization; Bad configuration'; } else if( (FailSafe & 2) != 0 && (FailSafe & 4) != 0 && (FailSafe & 8) != 0 ){     return 'Bad visibility; Initialization; Bad configuration'; } else if( (FailSafe & 1) != 0 && (FailSafe & 2) != 0 ){     return 'Detection zones staying in a given state; Bad visibility'; } else if( (FailSafe & 1) != 0 && (FailSafe & 4) != 0 ){     return 'Detection zones staying in a given state; Initialization'; } else if( (FailSafe & 1) != 0 && (FailSafe & 8) != 0 ){     return 'Detection zones staying in a given state; Bad configuration'; } else if( (FailSafe & 2) != 0 && (FailSafe & 4) != 0 ){     return 'Bad visibility; Initialization'; } else if( (FailSafe & 2) != 0 && (FailSafe & 8) != 0 ){     return 'Bad visibility; Bad configuration'; } else if( (FailSafe & 4) != 0 && (FailSafe & 8) != 0 ){     return 'Initialization; Bad configuration'; } else if( (FailSafe & 1) != 0 ){     return 'Detection zones staying in a given state'; } else if( (FailSafe & 2) != 0 ){     return 'Bad visibility'; } else if( (FailSafe & 4) != 0 ){     return 'Initialization'; } else if( (FailSafe & 8) != 0 ){     return 'Bad configuration'; } else {     return 'Unknown' }
    </blockquote>
    Neither is a good replacement for a  `switch` statement, a message array or string concatenation.
    
    <i>Edit: some code formatting mistakes</i>
    <i>Edit 2: added "associativity" in response to VinDuv's post</i>
    <hr>Filed under:<div align="center"><kbd>Boredom level: <meter min="1" value="7" max="10" title="7 out of 10">7 out of 10</meter></kbd></div>
    
  • (disco) in reply to aliceif
    aliceif:
    However, the introduction text is something I vehemently disagree with.
    +1
    Mark Bowytz:
    I have had zero use of ternary operators ... The only practical application of ternary operator is to either intentionally obfuscate your code... ... Case in point, the single line of code

    These remarks translate into the following logic:

    "I have not used feature X." "I have seen an example of feature X abused." "Ergo feature X must be bad".

    Which might be a wtf in its own right.

  • (disco) in reply to Zecc
    Zecc:
    I'm assuming this is a language with sane precedence — ie, not **C or any later language which copied its rules**
    FTFY
  • (disco)

    TRWTF is not the ternary operators instead of if-else (although ternary operators for complex if statements, especially nested ones, are certainly a wtf). It's that there's four different things representing known problems, any combination of which could be non-zero, and it tests for each possible combination - a total of 16 conditions, not counting all zero and anything other than those four being non-zero, even though it always does the same thing for, e.g. FailSafe&2 != 0 regardless of how many others there are.

    if (FailSafe == 0){
        return 'No technical alarms';
    }
    else {
        message = '';
    
        if ( (FailSafe & 1) != 0 ){
            message .= 'Detection zones staying in a given state';
        }
        [...etc]
    
        if (message == ''){
            message = 'Unknown';
        }
    
        return message;
    }
    

    Edit: slight improvement

  • (disco) in reply to VinDuv

    I meant associativity. PHP's ternary operator is unintuitively left-associative. I amended my post.

  • (disco) in reply to aliceif
    aliceif:
    The ternary operator is a great tool to make code more readable by dropping unnecessary syntactic verbosity.
    Terseness is not necessarily a virtue. As frequently used, the ternary is an effective tool for hiding if() statements or, as we see here, a switch-case. But hey, the worst ternary abuse I ever saw was this:
    if ( condition1 )
       condition2 ? some_var = some_expression : 0;
    else
       something_else;
    

    Some lazy sod couldn't be bothered to add the braces when converting the inner if() from a simple assignment.

    My main argument for avoiding the ternary is that it is so subject to abuse that allowing it outside of some strictly regulated circumstances is a really bad idea, sort of like how overt goto should be handled. Examples of reasonable ternaries are where you want to pass this or that parameter (depending on some condition) to a function without having to duplicate the rest of the function call. Unreasonable ones include constructions like in this WTF, but also things like this:

      (some_condition ? function1 : function2)(some, long, argument, list);
    

    That is, call the appropriate one of these functions with the same argument list.

    My main objection is not so much that ternaries hide conditions / if()s / switch-cases as that when reading the code I have to stop and decode them to work out what they are trying to do - the syntax is excessively quiet and the rules of precedence are a bit flaky across languages.

  • (disco)

    If this was a sane language that used C style precedence rules, you could write this very clearly as :

      FailSafe==0 ? "No technical alarms":
      FailSafe & 1 && FailSafe & 2 && FailSafe & 4 && FailSafe & 8 ? "Detection zones staying in a given state; Bad visibility;Initialization; Bad configuration":
      FailSafe & 1 && FailSafe & 2 && FailSafe & 4 ? "Detection zones staying in a given state; Bad visibility; Initialization":
      FailSafe & 1 && FailSafe & 2 && FailSafe & 8 ? "Detection zones staying in a given state; Bad visibility; Bad configuration":
      FailSafe & 1 && FailSafe & 4 && FailSafe & 8 ? "Detection zones staying in a given state; Initialization; Bad configuration":
      FailSafe & 2 && FailSafe & 4 && FailSafe & 8 ? "Bad visibility;Initialization; Bad configuration":
      FailSafe & 1 && FailSafe & 2 ? "Detection zones staying in a given state; Bad visibility":
      FailSafe & 1 && FailSafe & 4 ? "Detection zones staying in a given state; Initialization":
      FailSafe & 1 && FailSafe & 8 ? "Detection zones staying in a given state; Bad configuration":
      FailSafe & 2 && FailSafe & 4 ? "Bad visibility; Initialization":
      FailSafe & 2 && FailSafe & 8 ? "Bad visibility; Bad configuration":
      FailSafe & 4 && FailSafe & 8 ? "Initialization; Bad configuration":
      FailSafe & 1 ? "Detection zones staying in a given state":
      FailSafe & 2 ? "Bad visibility":
      FailSafe & 4 ? "Initialization":
      FailSafe & 8 ? "Bad configuration":"Unknown";  
    

    Brackets are superfluous as are !=0 conditions

  • (disco) in reply to rep_movsd
    rep_movsd:
    If this was a sane language that used C style precedence rules, you could write this very clearly as
    Ok, now rewrite "FailSafe & 1 && FailSafe & 2 && FailSafe & 4 && FailSafe & 8" to "FailSafe == 15", etc., and you'll see that it's just a list from 0..15.
  • (disco)

    Loving complexity of the operator here.

  • (disco) in reply to faoileag
    faoileag:
    +1

    "I have had zero use of ternary operators"

    These remarks translate into the following logic:

    "I have not used feature X."

    I read it as the author meant to type "I have had zero use for ternary operators".

    Which makes infinitely more sense.

  • (disco) in reply to xaade
  • (disco) in reply to TGV

    My exercise was to use only delete :smile:

  • (disco) in reply to CarrieVS

    Depending on the use case, you might care about the performance of string concatenation in frequently-occurring conditions.

  • (disco) in reply to antiquarian

    Not saying there IS zero use for...

    Just saying the phrase

    have had zero use of

    is less likely meant than

    have had zero use for

  • (disco) in reply to antiquarian
    faoileag:
    +1
    Mark Bowytz:
    I have had zero use of ternary operators ... The only practical application of ternary operator is to either intentionally obfuscate your code... ... Case in point, the single line of code

    These remarks translate into the following logic:

    "I have not used feature X." "I have seen an example of feature X abused." "Ergo feature X must be bad".

    Which might be a wtf in its own right.

    +1

    @mark_bowytz should consider an example such as:

    if (testValue == true)
    {
       setvalue = 0;
    }
    else
    {
        setvalue = 42;
    }
    

    Personally, I prefer the ternary operator version of that:

    setvalue = (testValue == true) ? 0 : 42;
    

    Ultimately, it means the same thing, but it's easier to see what's going on since you just read the one line.


    Steve_The_Cynic:
    My main objection is not so much that ternaries hide conditions / if()s / switch-cases as that when reading the code I have to stop and decode them to work out what they are trying to do - the syntax is excessively quiet and the rules of precedence are a bit flaky across languages.

    As for the stopping to decode them, that probably has more to do with how often you encounter them than anything. I use them all the time in my code, and I have no problem decoding (properly) used ternary operators. Ones like the monstrosity in the article take a while though.

    As for the rules of precedence being flaky across languages, that's why I generally avoid using ternary operators in situations that would require multiple levels. If you have a simple, single level use case, the rules of precedence tend to be pretty clear.


    antiquarian:
    Not really.

    Ah, the Blub paradox. I think you might be right that @mark_bowytz may be dealing with such a scenario.

  • (disco) in reply to xaade

    I understood you. I'm saying that going from "had zero use of" to "had zero use for" isn't much of an improvement.

  • (disco) in reply to abarker
    abarker:
    if (testValue == true)
    {
       setvalue = 0;
    }
    else
    {
        setvalue = 42;
    }
    

    Personally, I prefer the ternary operator version of that:

    setvalue = (testValue == true) ? 0 : 42;
    

    And if you are setting a hitherto undeclared variable using the ternary form is even more elegant:

    my $answer = 0;
    if ($book->isHHGTTG()) {
      $answer = 42;
    }
    ```
    looks clumsy against:
    
    ```perl
    my $answer = $book.isHHGTTH() ? 42 : 0;
    ```
    
  • (disco) in reply to mruhlin
    mruhlin:
    Depending on the use case, you might care about the performance of string concatenation in frequently-occurring conditions.
    ``` {     /* .... */  "No technical alarms",     /* ...1 */  "Detection zones staying in a given state",     /* ..2. */  "Bad visibility",     /* ..21 */  "Detection zones staying in a given state; Bad visibility"     /* .4.. */  "Initialization",     /* .4.1 */  "Detection zones staying in a given state; Initialization",     ...     /* 8421 */  "Detection zones staying in a given state; Bad visibility; Initialization; Bad configuration" }[FailSafe]``` (substitute `{ }` with `[ ]` depending on language)
  • (disco) in reply to abarker
    abarker:
    ```c# if (testValue == true) { setvalue = 0; } else { setvalue = 42; } ```

    Comparing boolean-valued things to true is a form of insanity. It isn't (normally) a problem in languages like C# or Java, but in C it is particularly dangerous - lots of values are (by the normal definitions of the language) true, but only one of them is equal to TRUE. All the other non-FALSE values are simultaneously true (not equal to FALSE) and false (not equal to TRUE). In C++ it is dangerous for a slightly different reason. I might write if ( k == true ) but if k is not a bool, I will have problems because C++ will silently coerce true to 1 before comparing it to k, rather than the other way around.

    No, in general, you should compare against false as in if ( k != false ) or even just not do the comparison, if ( k ).

    For real fun, though, consider the sinners who write an "operator bool()" member function in a C++ class, where that class might reasonably be used as a key in an STL container such as map or set. The map becomes effectively a map<bool, OtherThing> and therefore holds only two values. Member operator bool() is a favorite of people who are creating smart pointer objects. They should convert to a non-useful pointer instead: operator class ZogShallRule *() where ZogShallRule is defined nowhere, or even declare ZogShallRule as a private inner class:

    class DodgySmartPointer
    {
      private:
        class ZogShallRule;
    
      public:
        operator ZogShallRule *() const { return reinterpret_cast<const ZogShallRule *>(this); };
    ...
    };
    

    You can still use DodgySmartPointer variables nakedly in if() and while() conditions ( if( dsp ) ) but this method doesn't cause havoc in collections.

  • (disco) in reply to Steve_The_Cynic
    Steve_The_Cynic:
    Comparing boolean-valued things to true is a form of insanity. It isn't (normally) a problem in languages like C# or Java, but in C it is particularly dangerous - lots of values are (by the normal definitions of the language) true, but only one of them is equal to TRUE. All the other non-FALSE values are simultaneously true (not equal to FALSE) and false (not equal to TRUE). In C++ it is dangerous for a slightly different reason. I might write if ( k == true ) but if k is not a bool, I will have problems because C++ will silently coerce true to 1 before comparing it to k, rather than the other way around.

    C and C++ are dangerous. We've had type-safe alternatives for years.

  • (disco) in reply to Steve_The_Cynic
    Steve_The_Cynic:
    It isn't (normally) a problem in languages like C# or Java, but in C it is particularly dangerous - lots of values are (by the normal definitions of the language) true, but only one of them is equal to TRUE.

    Yeah, and then you spend two hours debugging your program before you notice that if (P1IN & 0x80 == TRUE) doesn't really do what you'd want it to. Twice. Been here, done that.

    And ternaries are insanely useful. Let's say you have f(string x, int y), and you want to pass a "YES"/"NO" and 1/0 value depending on some conditions. Would you rather write:

    if (a == 0 && b < 42)  f("NO", 1);
    else if (a != 0 && b < 42) f("YES", 1);
    else if (a == 0 && b >= 42) f("NO", 0);
    else f("YES", 0);
    

    or

    f( a == 0 ? "YES" : "NO", b >= 42 ? 0 : 1 );
    

    ? To me, it's pretty much a no-brainer.

    antiquarian:
    C and C++ are dangerous. We've had type-safe alternatives for years.

    When doing low-level stuff, it's sometimes useful to just hack away without the regard for type safety. As long as you know what you're doing, that is.

  • (disco) in reply to Steve_The_Cynic

    In a way, it's even more dangerous with type-safe languages like Java and C#, where accidentally using a single equals sign in an if statement would normally cause a compile error, unless you happen to be comparing a boolean variable to a boolean literal.

  • (disco) in reply to shmosel
    shmosel:
    unless you happen to be comparing a boolean variable to a boolean literal.

    Which you shouldn't be doing in the first place.

  • (disco) in reply to Maciejasjmj

    My point precisely.

  • (disco) in reply to Maciejasjmj

    Here's another example of useful ternaries. They're nested, even! Before Perl 5.12 (I believe), Perl didn't have switch or case. If you wanted to switch, you would have to do:

     ( case1 ? result1
     : case2 ? result2
     : case3 ? result3
     : case4 ? result4 
     : noMatch()
     );
    

    This should not be hard to read. It is a very informative structure.

  • (disco) in reply to Captain

    Your last line seems to be a binary operator.

  • (disco) in reply to Captain
    Captain:
    This should not be hard to read. It is a very informative structure.

    I always keep reading ternaries as questions. Something like "Case1? Yes? Ok, then result1." etc. It's pretty intuitive, in a way.

    I also like C#'s x ?? somevalue - basically, a stripped-down x == null ? somevalue : x. Another one of the more useful statements.

  • (disco)

    Sure the code is messed up. However, another WTF is someone submitting code who may have been stuck in their ways enough to utter this opinion: "The only practical application of ternary operator is to either intentionally obfuscate your code or use it as a soapbox to brag about how "l33t" you are.".

    At least here it's a pain to read through, but everything is clear past the formatting. If this had been something along the lines of a ridiculous lambda expressions using implied data types, then I might have agreed (some people shouldn't be allowed to have ReSharper... :-P ).

  • (disco) in reply to shmosel

    Oops, fixed. :)

  • (disco) in reply to Maciejasjmj

    Or JS's ||

  • (disco) in reply to Maciejasjmj

    I always keep reading ternaries as questions. Something like "Case1? Yes? Ok, then result1." etc. It's pretty intuitive, in a way.

    Exactly. And under that reading, : is "exclusive or". Haskell's pattern guards look like:

    abs :: Num a => a -> a
    abs x | x > 0 = x
          | x <= 0 = -x
    

    They've got almost the same amount of noise (very little to none)

    I also like C#'s x ?? somevalue - basically, a stripped-down x == null ? somevalue : x. Another one of the more useful statements.

    That looks like a prism combinator. Does C# have a generalized framework for constructing prisms?

  • (disco) in reply to Zecc

    I would bet that in the code it actually is done like you posted, all spaced out and legible. Then either the submitter or Mark removed the whitespace and newlines to make it extra WTFy. Or a codeshrink utility (if it's an interpretted language) removed all the whitespace and newlines.

    By the way, nice article, that massive ternary mess stands alone (even if it had been whitespaced properly, it's still nasty) and doesn't need a 20 page backstory. Well done.

  • (disco) in reply to faoileag
    faoileag:
    I have not used feature X.

    And this is where your wrong assumption undermines your argument. He's saying that he hasn't had a situation where it's useful, not that he hasn't used it.

  • (disco) in reply to abarker
    abarker:
    @mark_bowytz should consider an example such as:

    if (testValue == true) { setvalue = 0; } else { setvalue = 42; }

    Meah.

    if (testValue) setvalue = 0;
    else setvalue = 42;
    
  • (disco) in reply to chubertdev
    setvalue = 42;
    if (testValue)
        setvalue = 0;
    
  • (disco) in reply to ben_lubar
    setvalue = testvalue << 1 | testvalue << 3 | testvalue << 5
    

    Filed under: Optimization!

Leave a comment on “One Bad Ternary Operator Deserves Another”

Log In or post as a guest

Replying to comment #:

« Return to Article