- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
Ternaries, bah.
Write it long form to start with, then when you know it works (which is when you've done all your unit tests - you have done your unit tests, haven't you?), you can start thinking ab out whether some of the if-else-endif patterns would benefit from being streamlined into a ternary.
Then test it again.
One is reminded of a meme that goes something like:
"Hey you there: Test it. And when you get there, test it from there too. Then test it some more. Keep testing it until you get back here. Then test it again."
except that instead of "test it" it says something rude meaning "go away". I'm sure you know which one I mean.
Admin
I loved learning how to write ternaries, but I never use them. They might look cool or more efficient, but they present more obfuscation for someone learning the system. Better to write long-form and make it clear!
Admin
I actually like using ternaries. But you know what a ternary's best friend is? Parentheses.
Admin
I can't figure out any operator precedence rules that end up with that line making sense. No matter what I come up with, it always results in a boolean operation on an integer.
Admin
Honestly, I don't consider ternaries themselves a big issue. Unlike an
if/elif/else
chain, they guarantee, that a value is assigned, which help readability in my book.Let's see...
With ternaries, a little code formatting goes a long way.
The issues do however start when using ternaries for relatively complex conditions. Also, I'm not aware of any editors, that have good support for formatting ternary expressions, or even seeing good conventions.
Heck, I still can't find anything better than
for Python.
Admin
I just checked the Terraform docs and the Ternary operator is called a "conditional expression" and has a separate section to all the operators. So I assume that it has lower precedence than any operator. This means it looks something like this:
So
count
will either be assignedlength(var.listener)
or0
but unless 0 automatically casts to false, there is an error in0 && var.internal == false || var.provision == true
. Perhaps it doesn't matter because short circuiting probably means the conditional never gets executed becausetrue || anytihng
istrue
Addendum 2022-03-30 08:27: That last bit is bollocks, sorry. Confused about
== true ||
and= true ||
Admin
Ternaries in my mind are good when they clearly express the intent. If the intent is to choose between two values based on a boolean then a ternary is the way to go. If/Else dictates control flow, and using control flow just to pick a value is less expressive of the intent.
Nested ternaries get ugly, but nested any operators get ugly. We have the good fortune that addition, multiplication, "and", and "or" are all associative and communitive, which obscures some of the ugliness, but determining the actual grouping and order of operations depends on subtle and rarely read sections of the language spec.
The solution to the nested ternary problem is the same as the solution to the nested any expression problem. Extract the subexpressions into their own method, or their own variable if you have to, and give that thing a clear, expressive name. No nested ternaries, no confusion about grouping-- and any decent compiler will inline it anyway if it helps you.
In my many decades of programming I have found that the "never use this confusing language feature" crowd to generally be less informed. (With one exception -- goto really is considered harmful!) Everything from ternaries, to pre/postincrements, to exceptions, to more than one exit per function has been dubbed by someone as too confusing for ordinary mortals. I say phoey on all of them. Every language feature has a purpose, and every language feature can be misused to make code unreadable. Use the language, and all the language, to express what you mean. Then break it up into small enough named functions and variables that that meaning is obvious to someone fluent in the language you are using.
Admin
Even
goto
is apparently not as universally bad, as I used to think.I have recently learned, that it is a best-practice of sorts for “found error code, go to cleanup section of the function” in C.
Prior to this, I had somewhere picked up the pattern
and was subsequently recommended, that this constitutes one of the few cases, where
goto
is the right choice, e.g.Also an argument for the “single point of return” rule...
Addendum 2022-03-30 08:47: By the way, the style-sheet does funny things with
<pre/>
blocks.Admin
You gotta love how the Hashicorp guys started out with the noble intent of creating a 100% pure declarative IaC language and quickly realized the unmaintainable, copypasty mess they would create, so they started adding all sorts of hacky features to it to end up with a slightly more dynamic but even more unmaintainable mess.
Admin
But... But... But those need the SHIFT key!
Admin
Say what you like about PHP, but the PHP 8.0 designers made a good decision: Nested ternaries are required to use parentheses.
Admin
It looks to me as if the person who wrote the expression thought that the ternary operator had a higher precedence than "&&" and "||" - I think it was intended as an and/or expression with 2 of the sub-expressions being ternaries
Ternaries (and even binary infix operators) suck whenever the expressions are non-trivial. The best solution by far is lisp/clojure - no operator precedence and all your nesting and layout problems are trivially solved no matter how complex the expressions and sub-expressions are
Admin
There is one more example of a really bad language feature: javascript's "with". It's use is even forbidden in "strict mode" and the ECMAScript Language Specification explicitly discourages its use.
Admin
Am I the only misbehaving coder who occasionally writes
Admin
So do the ":" the "?" the "&" and the "|", so I don't see the problem here.
Admin
As Neil Degrasse Tyson said, before trying to terraform, much easier to fix our own place.
Admin
No, probably not. Though I've mostly used it in Fortran to make optional-argument initialization a one-liner.
I've also run into situations where the scoping rules of the language forbid
Addendum 2022-03-30 12:43: Point of the latter being: At this point it is attractive to do
though it would still be clearer, if more verbose, to do
Admin
I'm confused, I don't know a language where "&&" and "||" have a higher precedence than most operators, while "?:" generally has the highest of all operators.
So this code is basically:
count =
((var.internal == true || var.provision == true) ? (length(var.listener)) : (0)) && ((var.internal == false || var.provision == true) ? (length(var.listener)) : (0))
I find languages these days odd that allow to mix up booleans with integers but apparently this is fine in this language.
Admin
I beg to differ, it's all about formatting your code in a proper way. if statements can be super verbose compared to the compact better readable ?: operator - as long as you don't write spaghetti code, especially with lambdas.
Admin
I'm going to sit here and laugh at you for believing a total myth.
Ternaries do not guarantee that. Seen in an actual codebase:
What does that assign if
x >= 5
? (In reality it was an attempt to avoid writing braces round an innerif()
that didn't need anelse
.I've also seen:
Where
func1
andfunc2
were actual function names, not pointers-to-functions.Admin
I actually like the ternary function choice!
Admin
"Am I the only misbehaving coder who occasionally writes
[code]
x = TRUE
if(something) x = FALSE [code] ?"
Absolutely not at all. I use it all the time. Sometimes it just makes more sense to do it that way.
You can't explain under what circumstances it does make sense so to do, it just feels right.
More and more I've programming by aesthetics nowadays. I can't begin to explain those aesthetics.
I even have a good case for the use of GOTO. There is a time and a place. Knowing where and when is again something I can't easily explain.
Admin
To be fair, a lot languages do not have return values for assignments; I remember C/C++ have them but for example C# does not, so your example would result in a compiler error.
Another horror construct back in the C/C++ days was a=b=c++ or worse a=b,c++
Admin
You've misinterpreted R3D3's comment. A ternary operator expression always produces a value, so if you use one on the right side of an assignment it is guaranteed that the variable will always get some value. On the other hand, if/else is usually a statement and so does not produce any value. Now, if you choose to not use the result of the ternary expression it just means the returned value is thrown away.
With respect to your examples,
(x < 5) ? a = b + c : 0;
produces the value 0 when x >= 5. The ridiculous use of an assignment in the ternary expression doesn't change what R3D3 said. If this expression were on the right side of an equals sign, the variable on the left side would be assigned the value 0. Now,
is a creative way to switch the function to be called, but still produces either func1(some, args, here) or func2(some, args, here). Again, you can choose to use the value or not, but it is always there. By the way, if you're writing in C, function names implicitly get converted to pointers to those functions when used in expressions or argument lists, so it doesn't matter whether you use f or &f in a context like this.
Admin
I've been working with Terraform a lot recently. Some things to note:
There is no if statements in Terraform/HCL. The way to do this is is with this ternary. "count" is reserved keyword used to determine the number of resources of that type to create. It must be a number and greater or equal to 0. Booleans in Terraform get automatically converted between boolean and strings but I don't see anything about converting booleans to integers.
For variables of type boolean, they can be set to true, false, or everyone's favorite, null. The
||
and&&
operators will error out if passed a null.I ran some tests using Terraform console in v1.1.7 and here is the output for various input values:
If var.internal is true and var.provision is true, count will get assigned the value of length(var.listener) If var.internal is true and var.provision is false, count will get assigned the value of length(var.listener) If var.internal is false and var.provision is true, count will get assigned the value of length(var.listener) If var.internal is false and var.provision is false, I get an "Error: Invalid operand, Unsuitable value for left operand: bool required." This looks to be because it is trying to use the zero as an operand to the '&&'.
Admin
I actually just suggested to a coworker that they consider using
if (x) { y = z; } else { y = null }
rather thany = null; if (x) { y = z;}
for clarity. But it was definitely more something to think about than a definite rule. (In this particular case there was more space between the two assignments than there is here, making it easy to miss the fact that they're essentially alternate code paths.)Admin
I don't mind complex ternaries. Don't even think of doing a non-trivial ternary without formatting the text to show the actual structure. To do otherwise is to ask for a problem like this.
Admin
A ternary would be clearer than either of those! y = (x) ? z : null;
Ternaries are awesome, complex boolean expressions are TRWTF.
Admin
@John Melville ref
Overall I agree with your thinking. Well said. But ...
There is an issue in your very last quoted sentence. You've made an implicit assumption the other employees working your codebase are fluent in the language. In my decades of experience, that's the assumption most often falsified by the reality around me.
Admin
Too relatable. Our code base is full of misunderstood language features, and unnecessary boilerplate dating back to older language versions, but still being added in new code.
Admin
Also, I keep reading stories along the lines of "I was assigned this internal code for maintenance, and I have never touched that language before."
Admin
Yes, they do. But they're so far up on the keyboard..... Point being, there's always another excuse for not doing the right thing or, at least, something "sensible".
Admin
One case where you need “x == true” is in Swift if you use optional bool: An optional bool can be true, false or nil. You can’t use an optional bool in an if statement. b = true means true, not nil, not false. b != false means not false, but true or nil. b == nil means nil, not true to false. b != nil means true or false, not nil.
Admin
I don’t actually care about the precedence of ?: vs && or ||. I assume that the reader, the writer or both got it wrong so you need parentheses.
Admin
I think so too. Of course that just means there are two more anti-patterns on display here (beyond bool==true and bool==false comparisons).
(bool || cond1) && (!bool || cond2) is just a complicated way of writing bool ? cond2 : cond1
And then you get the fact that cond1 and cond2 are the same, so under this interpretation the whole thing just collapses to the inner ternary:
count = var.provision ? length(var.listener) : 0.
Admin
very nice
Addendum 2023-09-21 22:01: Imagine my excitement when I stumbled upon a website that curates a comprehensive list of the finest homework writing services available. This online resource best essay writing services https://uktopwriters.com/best-essay-writing-services/ is a goldmine for students seeking expert assistance with their assignments. With meticulous research and expert reviews, the website showcases a carefully curated selection of top-notch services. It's a game-changer for anyone looking to make informed decisions when it comes to academic support, ensuring that students can access the best help available to excel in their studies.