19

Ahh, don't you just love a good ternary abuse? :) Consider the following expression:

true ? true : true ? false : false

For those of you who are now utterly perplexed, I can tell you that this evaluates to true. In other words, it's equivalent to this:

true ? true : (true ? false : false)

But is this reliable? Can I be certain that under some circumstances it won't come to this:

(true ? true : true) ? false : false

Some might say - well, just add parenthesis then or don't use it altogether - after all, it's a well known fact that ternary operators are evil!

Sure they are, but there are some circumstances when they actually make sense. For the curious ones - I'm wring code that compares two objects by a series of properties. It would be pretty nice if I cold write it like this:

obj1.Prop1 != obj2.Prop1 ? obj1.Prop1.CompareTo(obj2.Prop1) :
obj1.Prop2 != obj2.Prop2 ? obj1.Prop2.CompareTo(obj2.Prop2) :
obj1.Prop3 != obj2.Prop3 ? obj1.Prop3.CompareTo(obj2.Prop3) :
obj1.Prop4.CompareTo(obj2.Prop4)

Clear and concise. But it does depend on the ternary operator associativity working like in the first case. Parenthesis would just make spaghetti out of it.

So - is this specified anywhere? I couldn't find it.

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • Are you sure your code does what you mean? E.g. if obj1.Prop1 != obj2.Prop1 but obj1.Prop1.CompareTo(obj2.Prop1) == 0, your code will yield 0 instead of proceeding to check Prop2. Is it intended? – atzz Nov 19 '09 at 14:18
  • 5
    Just because you like the way it looks doesn't make it right, and certainly doesn't make it readable.. Anybody else looking at that code will have to at least mentally add parenthesis anyway, so why not do everybody a favor and refactor those lines of code. – Mike Dinescu Nov 19 '09 at 14:19
  • atzz - The values are primitive types - this shouldn't happen unless there is a warp in spacetime. Miky D - suggestions? Keep in mind that this is for a lambda expression, so I'd like to keep it short. Also I'll add comments, which should clarify things for anyone looking at the code. – Vilx- Nov 19 '09 at 14:21
  • Well, every situation is different and without looking a the rest of the code I can't make any intelligent suggestions but maybe you could create a function that performs the comparisons and call that in the lambda.. – Mike Dinescu Nov 19 '09 at 14:25
  • I need to use it in one place only. I could, of course, extract it into a function, but there are many such instances in my code (it synchronizes two DB's) and I'd end up with a dozen such functions. – Vilx- Nov 19 '09 at 14:28
  • 6
    "Also I'll add comments, which should clarify things..." don't comment unreadable code. Make the code readable (where possible of course). It is certainly possible in this instance. – Binary Worrier Nov 19 '09 at 14:30

5 Answers5

25

Yes, you can rely on this (not only in C# but in all (that I know) other languages (except PHP … go figure) with a conditional operator) and your use-case is actually a pretty common practice although some people abhor it.

The relevant section in ECMA-334 (the C# standard) is 14.13 §3:

The conditional operator is right-associative, meaning that operations are grouped from right to left. [Example: An expression of the form a ? b : c ? d : e is evaluated as a ? b : (c ? d : e). end example]

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 5
    A note on readability. Anyone with a functional programming background will immediately recognize this (it looks almost precisely like Haskell’s guard clauses with appropriate formatting). So while it might not be universally acceptable and usable, it certainly *is* idiomatic in certain circles and may be very appropriate in some projects. On a more personal note, *I* use it in personal projects, I find it *vastly* more readable than chained `if`s, and if you can’t read it then get out of my code. – Konrad Rudolph Nov 19 '09 at 16:45
  • "in certain circles [or projects]" is key! –  Nov 19 '09 at 18:18
  • You can't rely on it in PHP: http://stackoverflow.com/questions/1921422/php-nested-conditional-operator-bug – Mark Rushakoff Dec 17 '10 at 02:15
  • Chased a bug for 4 hours that was caused by PHP's unique associativity rules for ternary (left to right). @Konrad: You should update the answer so that it's accurate in regards to PHP – Ruan Mendes Mar 14 '11 at 23:17
  • 2
    @Juan What, I should write that PHP sucks? That seems a bit off-topic. And it’s not very interesting … everybody knows *that*. – Konrad Rudolph Mar 15 '11 at 22:26
  • 1
    @Konrad: PHP does suck, but you're still giving misleading information in your answer, since you do know that PHP does not behave that way. – Ruan Mendes Mar 16 '11 at 01:26
16

If you have to ask, don't. Anyone reading your code will just have to go through the same process you did, over and over again, any time that code needs to be looked at. Debugging such code is not fun. Eventually it'll just be changed to use parentheses anyway.

Re: "Try to write the whole thing WITH parentheses."

result = (obj1.Prop1 != obj2.Prop1 ? obj1.Prop1.CompareTo(obj2.Prop1) :
         (obj1.Prop2 != obj2.Prop2 ? obj1.Prop2.CompareTo(obj2.Prop2) :
         (obj1.Prop3 != obj2.Prop3 ? obj1.Prop3.CompareTo(obj2.Prop3) :
                                     obj1.Prop4.CompareTo(obj2.Prop4))))

Clarification:

  • "If you have to ask, don't."
  • "Anyone reading your code..."

Following the conventions common in a project is how you maintain consistency, which improves readability. It would be a fool's errand to think you can write code readable to everyone—including those who don't even know the language!

Maintaining consistency within a project, however, is a useful goal, and not following a project's accepted conventions leads to debate that detracts from solving the real problem. Those reading your code are expected to be aware of the common and accepted conventions used in the project, and are even likely to be someone else working directly on it. If they don't know them, then they are expected to be learning them and should know where to turn for help.

That said—if using ternary expressions without parentheses is a common and accepted convention in your project, then use it, by all means! That you had to ask indicates that it isn't common or accepted in your project. If you want to change the conventions in your project, then do the obviously unambiguous, mark it down as something to discuss with other project members, and move on. Here that means using parentheses or using if-else.

A final point to ponder, if some of your code seems clever to you:

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. — Brian W. Kernighan

  • It's for a lambda expression. Other ways would be only marginally better IMHO. – Vilx- Nov 19 '09 at 14:16
  • 4
    Absolutely agreed. If you have to ask, then it's not clear. If it's not clear, then put f-ing parentheses around it and stop being a clever programmer. That's where the worst bugs come from. – Gabriel Magana Nov 19 '09 at 14:17
  • 1
    I disagree that it is a good approach. It's not idiomatic and someone who has not encountered it needs to examine it closely to see what's going on. A series of if/then clauses is clearer. – mqp Nov 19 '09 at 14:19
  • yes, Anything I'M not familiar with, (or are too stuck in my ways to learn), must be 'clever' programming... – – Charles Bretana Nov 19 '09 at 14:22
  • Well... it's a fair point, of course. Would comments help? :) As I said, the reason for this is because I'm writing this in a lambda expression, so I'd like to keep it short, resembling a formula more than a piece of code. – Vilx- Nov 19 '09 at 14:25
  • 1
    Vilx: Why comments instead of parentheses? Which way is more obvious and least likely to result in docs/comments differing from code? –  Nov 19 '09 at 14:27
  • Try to write the whole thing WITH parentheses. Personally, I'd prefer the row of IF statements then. It's more verbose, but at least cleaner. – Vilx- Nov 19 '09 at 14:32
  • Charles: What do you have to judge by, except yourself? It's a fair assumption that if he had a hard time deciphering the code (and I use 'decipher' intentionally), then anyone else *working on his project* will too. –  Nov 19 '09 at 15:24
  • @Roger, no it's not a fair assumption. When I look at Lisp code I have no clue what it means. That doesn't mean it's bad code. Just cause someone doesn't like ternary operators, doesn't use them, and is therefore unfamiliar with reading them, doesn't mean they are unreadable... The same thing applies to lambdas the first few times I looked at them ... It's ok if someone doesn't want to use them, I can't (and wouldn't) try to force anyone to use anything they're uncomfortable with. Just don't disparage a tool because you don't like it and/or are not fluent or competent in it's use. – Charles Bretana Nov 19 '09 at 16:13
  • Do you often work on Lisp projects when you have no knowledge of Lisp? –  Nov 19 '09 at 16:18
  • @Roger, how is that relevant? The point is that it's not readable because I'm not familiar or competent with it, not because it's bad code. The same is true for C/C# code which uses new features, like Generics/anonymous methods in C#2.x, or Lambdas in 3.x, until I take the time to learn them, and use them sufficiently to become fluent with them. – Charles Bretana Nov 19 '09 at 16:54
  • Following the conventions common in a project is how you maintain consistency, which improves readability. It would be a fool's errand to think you can write code readable to everyone---including those who don't even know the language! But maintaining consistency within a project is a useful goal. When you break a project's accepted conventions you lead to debate that detracts from solving the real goal. Just look at all the effort spent on this question, versus how long it would take to add parentheses and move on. –  Nov 19 '09 at 17:21
  • FWIW, I made a conscious decision not to learn the C operator precedence table, on the principal that if I had to learn the table to parse my expressions it would confuse other people (and maybe me, later on). If it isn't obvious, I parenthesize it. – David Thornley Nov 19 '09 at 18:29
  • So, what constitutes readable, or unreadable, good, or bad, code is dependant on an individual teams' consistency standards, and not on the code itself? I guess you're free to define anything anyway you want, but that's not how I would define a quality attribute. to me, the definition has to be such that it is dependant on characteristics internal to whatever is being evaluated, not on external factors which can differ from one setting to another. – Charles Bretana Nov 19 '09 at 19:09
  • In this forum we are not on a single team, with a single standard. SO making statements which purport to judge qualities like readability, or clarity, when your personal definition is dependant on an internal standard which others here may or may not subscribe to or use, would be .... not as clear ??? – Charles Bretana Nov 19 '09 at 19:11
  • Charles: It depends on context, of course. How anything is measured depends on the requirements, and those are different for software making my coffee, controlling the space shuttle, or doing my taxes. We are not on a single team, but I can deduce it is neither common nor accepted *for his project* **because** he had to ask. –  Nov 19 '09 at 19:29
  • @Roger, well, his question was about the precedence order, and, contray to your statement about his feelings, to quote him " It would be pretty nice if I cold write it like this: , Clear and concise!" That tells me just the opposite of what you are saying. He would prefer to use the ternary! But this is still not relevant to my point which is that software quality be it readability, or clarity, or whatever, does not, should not, depend on subjective judgments. If he said If Else clauses are evil, does that make them so? Of course not, no matter if his team treats them as anathema. – Charles Bretana Nov 19 '09 at 20:06
  • If his project had a specific convention not to use else clauses, then he should either 1) not use else clauses, or 2) get that convention changed before using else clauses. This is assuming that he wants to solve the problem his project set out to solve---if he just wants to spend all day arguing with other team members, then he should do as you say. We will just have to disagree, because no matter how *you* judge code, doesn't necessarily make *your criteria* right for any other project. –  Nov 19 '09 at 20:26
  • "if he just wants to spend all day arguing with other team members, then he should do as you say.." ???? your chutzpa is astounding... You just assume that your judgment is universal and that anyone who disagrees is just a argumentative iconoclast ? Then you clearly miss my point totally, and after making several attempts now to communicate it, I guess we will have to agree to stop communicating... (I can't say agree to disagree, cause frankly, I have no sense from you that you have any idea what it is I disagree with you about... ) – Charles Bretana Nov 20 '09 at 01:20
  • You get my +1 for quoting `Brian W. Kernighan`. – RBT Oct 17 '16 at 01:03
4

The assertion that parentheses detract from the readability of the code is a false assumption. I find the parenthetical expression much more clear. Personally, I would use the parentheses and/or reformat over several lines to improve readability. Reformatting over several lines and using indenting can even obviate the need for parentheses. And, yes, you can rely on the fact that the order of association is deterministic, right to left. This allows the expression to evaluate left to right in the expected fashion.

obj1.Prop1 != obj2.Prop1
     ? obj1.Prop1.CompareTo(obj2.Prop1)
     : obj1.Prop2 != obj2.Prop2
           ? obj1.Prop2.CompareTo(obj2.Prop2)
           : obj1.Prop3 != obj2.Prop3
                  ? obj1.Prop3.CompareTo(obj2.Prop3)
                  : obj1.Prop4.CompareTo(obj2.Prop4);
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 2
    Re: your last sentence. I note that the question wasn't about order of evaluation, it was about associativity. Those two things are completely different. – Eric Lippert Nov 19 '09 at 14:51
  • I should have been more clear that it associates in a way that allows the expression to be evaluated left to right in the expected fashion. – tvanfosson Nov 19 '09 at 15:43
2
x = cond1 ? result1
  : cond2 ? result2
  : cond3 ? result3
  : defaultResult;

vs

if (cond1) x = result1;
else if (cond2) x = result2;
else if (cond3) x = result3;
else x = defaultResult;

I like the first one.

Yes, you can rely on conditional operator associativity. Its in the manual, at the link kindly provided by dcp, stated as "The conditional operator is right-associative", with an example. And, as you suggested and I and others agreed, the fact that you can rely on it allows clearer code.

radu florescu
  • 4,315
  • 10
  • 60
  • 92
Tim
  • 21
  • 1
  • Yes, you can rely on conditional operator associativity. Its in the manual, at the link kindly provided by dcp, stated as "The conditional operator is right-associative", with an example. And, as you suggested and I and others agreed, the fact that you can rely on it allows clearer code. – Tim Dec 20 '10 at 03:29
1

Refer to msdn: http://msdn.microsoft.com/en-us/library/ty67wk28%28VS.80%29.aspx

"If condition is true, first expression is evaluated and becomes the result; if false, the second expression is evaluated and becomes the result. Only one of two expressions is ever evaluated."

dcp
  • 54,410
  • 22
  • 144
  • 164
  • 1
    True, but this doesn't say anything about associativity. – Vilx- Nov 19 '09 at 14:22
  • @Vilx - You are right, however, the associativity part of the question was added during an edit, after I had already given my answer (refer to the edit history on the question). The original question read: "But it does depend on the ternary operator precedence ". So my answer was based on precedence. – dcp Jan 31 '12 at 21:03
  • Wow. Revisiting old answers? :) Anyway, I'm still hazy myself about the "associativity vs precedence" thing, so - my apologies! :) – Vilx- Jan 31 '12 at 21:28
  • Vilx - Actually, I owe you an apology too. I responded because someone downvoted this today, so I thought it was you since yours was the only reponse. But now I realize your response was over 2 years ago. So I guess someone downvoted without providing a reason. Anyway, sorry for the confusion :) – dcp Jan 31 '12 at 21:33
  • 1
    Nop, I haven't given you any votes - up or down. But here - have an upvote. Just so. :) – Vilx- Jan 31 '12 at 21:40
  • @Vilx - Much obliged, and I really liked your answer to this question: http://stackoverflow.com/questions/9053940/sha-1-implementation-in-php-and-net/9054052#9054052, so I returned the favor :). – dcp Jan 31 '12 at 21:51