That pattern is the expression equivalent of if..; elsif..; elsif..; elsif...
Writing it out in long form using actual if statements doesn't add much clarity, and costs in verbosity, as the OP says, macro-clarity versus micro-clarity.
I agree with the sibling to this comment, though, that a ternary is more readable with newlines and indentation:
return (a < b ? -1
: a > b ? 1
: 0);
That's the style I use, except for extremely short and ternaries where the verbosity adds nothing, like (A > B ? A : B).In my view it becomes more complex to understand when there's a ternary inside the first branch, because then it's equivalent to if...(if...else...)...else... At that point I'd consider using if statements, if there is no reason to stay with an expression.
I think it's even clearer like so:
return a < b ? -1:
a > b ? 1:
0;
Conditions/guards on the left, values on the right. So I emphatically disagree with the OP that nested ternary expressions are never ok. In most sane languages with proper precedence and evaluation order, they work great when formatted as above. I say sane languages, because the nested ternary doesn't work the way you'd expect in JavaScript...I use the parantheses the way I do, because editors will auto-indent the code that way. In other words if I press <tab> in Emacs, things won't move around in my example, so I know it's indented properly.
However, within that style, some people do prefer to put the operator at the end of a continuation line, and some prefer it at the beginning.
Is this the lowest level of decision making in something like a large drawing application, or some CAD or financial package, then for the love of God, take the concise approach ! If you consistently take the longer approach, may God provide mercy on your soul (and a very large monitor) when you get to vector multiplication or matrix math.
If you're writing 3 business rules in something that's important and needs reliability and therefore should not have complexity ? Then it might be better to write it out.
(in both cases, because of the potential for stupid mistakes, I'd add tests)
But there's no single solution for all situations. High complexity software ? Concise will help out more. Low complexity software ? Write it out for clarity.
I'd give you multiple upvotes if I could for this.
Absolutely agree with all your points.
I'd add further, that if it's 3 important business rules, then sometimes expanding it further to have well-named functions and well-named variables is well worth doing, even if the business rules are trivial logic:
// This rule was recommended by the accountant on 2019-05-06
// and must be reviewed by the CFO before release.
function receipt_needs_itemised_tax_record(amount: Money): bool {
return amount >= 1.00;
}
// Show itemised tax records on receipts that need it.
if (receipt_needs_itemised_tax_record(receipt.total_paid)) {
...
}
Versus: // Writing this and other low-level code in 25 lines per function
// does not make the 10kloc rendering library easier to understand.
function transform_pixel(bg: RGB, fg: RGB, opacity): RGB {
opacity = clamp(opacity, 0.0, 1.0);
let blend_bg = 1.0 - opacity, blend_fg = opacity;
return RGB { r: clamp_rgb(bg.r * blend_bg + fg.r * blend_fg),
g: clamp_rgb(bg.g * blend_bg + fg.g * blend_fg),
b: clamp_rgb(bg.b * blend_bg + fg.b * blend_fg) };
}When they are, you basically get something that looks visually like a decision tree. This can read really nicely in situations where declarative style code fits better - for example embedding nested ternarys in JSX is quite a popular pattern for this reason.
The caveat is, you have to be 'used to' reading the ? and : symbols and instantly mapping them to if/else, but I certainly don't think getting used to this in a short time frame is beyond expectation for someone who's not already, i.e. new team members etc.
Six-months-ago-myself thought so as well. Turns out six-months-ago-myself is a goddamn moron.
It's a balancing act which I have found to be one of the more fulfilling aspects of my work. Having people of different skill levels review my code has made this much easier.
One thing that I do a lot though is format code in a way that certain patterns appear for those of us that like to skim code. This generally means avoiding ternary operations except for very simple things where that pattern is easily recognized. But when to do that depends on the team(s) involved.
But as I say above, I think in certain situations they can be more readable for everyone.
If they don't then they need to learn.
You can't say don't use specific language features because some devs can't be bothered to learn them.
“You can't say don't use specific language features because some devs can't be bothered to learn them.”
Would you say the same about C++? In my view it’s a good practice to use only a subset of a language consistently and not use all features. I have seen JavaScript code where I had to do research for half an hour before I could figure out what it really meant.
My view on this has flip flopped several times during my career. I think every spec has some dusty corners that most people don’t know about, but when a situation calls for it knowing about some obscure features can make your code far more readable. Sometimes it’s better in the long run to train your team about a feature they might not know about rather than code for the lowest common denominator. This thread is a great example - I spent my first decade of programming scared of chained terneries. One day I spent a couple of hours goofing around with them, playing with different ways I could write my code and internalising their semantics. Now they seem fine. I wish I’d taken the time to do that years ago. Ten years being afraid saved me 2 hours of time learning.
One of the best programmers I worked with reads the specs of tools he uses for fun. He says specs seem daunting but you can read them in far less time than you think and there’s always some fascinating stuff in there. My HTML knowledge got way better working with him - and its funny seeing how many tools struggle with correct HTML because the authors didn’t bother actually learning it.
Some more examples: html void elements, html/body tag auto insertion, JS tagged break/continue, C/JS/etc’s comma operator.
If something is so detailed that it gets too long to be quickly readable I extract it into a separate properly-named function.
I'm definitely drawn to using all the tricks of a language and I feel like it would be a great way to show off how smart I am, but I'm not sure its worth it for sacrificing comprehension and ease of adding stuff in later. Maybe if everyone that will ever work or use that code is top-notch, that would be a dream.
It's not whether some constructs are parts of the language (languages can have any crap in), it's whether some constructs are bad, confusing, and should be avoided (whether you know what they do or not).
Is there never going to be any level of depth in a ternary statement which you will think is perhaps too great, and want to switch to some more verbose syntax?
You could say that most programmers are crap, but those crap programmers seem to make the world work, so they must be doing something right :-)
Because they are
> but those crap programmers seem to make the world work
For certains value of "work". Most software is crap too and it's getting worse not better.
if (cond) {
var1 = x;
} else if (cond2) {
var2 = y;
} else {
var1 = z;
}
Did you notice the second block assigned to var2 instead of var1? Best case it’s a gotcha for anyone reading the code later. It might be a bug by the original author; we can’t tell at a glance if this behaviour was intentional. Ternaries remove this problem. The intent of the author is clearer, and (with proper formatting) there’s no gotchas for anyone reading the code later. var1 = if cond {
x
} else if cond2 {
y
} else {
z
}
Which in my opinion is strictly better than using a ternary, if the language supports it.My favorite part is that its easy to add extra statements into the conditional blocks if you want. Doing that with ternaries requires either using the comma operator, repeating the condition on another line or refactoring the whole expression back out into if-else chains. All of those options are bad.
When in Java, I've gotten into the habit of using the final initializer to mitigate these kinds of issues, like this:
final T var1;
if (cond) {
var1 = x;
} else if (cond2) {
var2 = y;
} else {
var1 = z;
}
In this specific case, the compiler will catch the lack of initialization in the second block. It's also very helpful with a complicated/nested conditionals to guarantee that you initialize the variable through every code path.Here's a blog post I'm becoming more and more persuaded by as time goes on:
https://medium.com/javascript-scene/nested-ternaries-are-gre...
return (
a < b ?
-1 :
a > b ?
1 :
0)But it doesn't mirror the if/else-if/else structure. Rather the mirrored semantics looks as follows:
if(a < b)
return -1;
else
if(a > b)
return 1;
else
return 0;
It's interesting that for all the claims in this thread about how obvious the ternary operator is, almost every single person got it wrong.Is there a functional difference in this example? No. There isn't. Is there a functional difference in any example? I don't think so, but I'm not 100% sure. And the nice thing about not being clever, I don't have to worry about it.
But why should that be the main thing to be concerned about and prioritise? In what way are they "misleading"? Is it really the state of the industry where a new person cannot learn a codebase, has no time or effort, and cannot read a single line of the language they were hired to work on, and that's the person we should build everything for?
Does it work like that in any other industry?
Yes, it does. Imagine you were a new underwriter at an insurance company and your predecessor used the shorthand nomenclature for every detail on the accounts you're taking over, and failed to write out any of the detailed reasoning behind why they chose to approve or deny claims. How much longer would it take for you to be able to fill their shoes than if they had been just a little bit more descriptive in their documents?
> "Programs must be written for people to read, and only incidentally for machines to execute." - Harold Abelson
Yet the software industry seems to have developed an illness of perception, that encourages the continual lowest-common-denominator dumbing-down of code and an aversion to learning. From what I've seen "collaboration" as a justification for this, and from the types of people who are proponents of it, it appears to be related to the effort of managers to attempt to turn developers into disposable and easily replaceable drones; something which everyone actually writing code should be against. In the trades, there's no lack of teamwork either, but the prevailing notion is "the least skilled/experienced learns from the most" --- only in software does there seem to be a strong counter-trend to that. Perhaps we should not encourage the degradation of our craft.
Maybe that quote should be modified slightly too: "Programs must be written for suitably qualified people to read"
Yes, but the jargon and language used is standardized; pilots don't get to put their own twist on e.g. formal communication with the tower when they come in to landing. You don't get to switch to idk, km/h when communicating your airspeed.
...so is the ternary operator.
I don't know what shorthand nomenclature of insurance underwriting is, but if it's a standard part of insurance underwriting then I would expect a new hire to be familiar with it, or need time to become familiar with it.
If it's something separate like actual Gregg or Pitman shorthand English which the one underwriter used personally, unrelated to underwriting skills, then I wouldn't compare that to a programming language's built-in ternary operators.
Which equals more overhead than is necessary. Elegance over accessibility is always more expensive, no matter the product. In my experience, folks who are obsessed with "perfect" code over readable code introduce the most long-lasting, destructive bugs, because, at best, nobody wants to and, at worst, nobody can decipher their stuff.
if (statement = ref) { print(statement) } else { print('have you done everything right?') }
was I wrong to be confused as to what he might have intended with statement = ref? Because he was in fact using the language functionalities to express what he wanted. I just thought it looked confusing and misleading.
test.c:3:17: warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
if (statement = ref) {
~~~~~~~~~~^~~~~
test.c:3:17: note: place parentheses around the assignment to silence this warning
if (statement = ref) {
^
( )
test.c:3:17: note: use '==' to turn this assignment into an equality comparison
if (statement = ref) {
^
==Seasoned 10x developers, titans of the industry, and all agree on simplicity and even they frequently make mistakes on easily confusable constructs such as nested ternaries (or even BS like forgetting a break in a switch etc).
It's not some bad "state of the industry" that calls for no such constructs.
If anything, it's amateurs and newbs, more self-confident than they should be (and less humble) that call for using any old crap in a language, and think that such constructs are only problematic if developers are "not paying attention" or are "ignorant".
As an extreme example, people familiar with mathematical notation have a hard time when they start using a programming language that uses '=' for assignment, because they intuitively understand it as asserting equality, which it isn't (x = x + 1 would be paradoxical). And yet we all here presumably think of assignment as something rather simple.
There are plenty of code bases where the ternary operator is used frequently. It's a more succinct (but less versatile) variant of the if/then/else expression that a number of languages have - do you find that confusing when nested?
The ternary operator is just as simple as if/then/else expressions. You may not be familiar with it, which can make it seem non-simple, but then we're just back to the observation that simplicity isn't entirely objective but subject to your background.
From my experience, and from my reading on tons of books, code, clean code suggestions, bug reports, etc, patterns of bugs are more commonly found on such code bits, that sacrifice clarity for succinctness or "clever points" (in fact that's the very premise of TFA).
Most respected elders in development also warn against those types of code.
Lacking objective research, that's the best we can get, and I'd say better than "it works for me".
It's much easier to go through a codebase if everyone uses the same code style and you get the least amount of surprises. This does however require you to give up your own ego, that is, you have to conform to the standard.