Hacker News new | past | comments | ask | show | jobs | submit login

I couldn't disagree more. Nested ternarys are NEVER okay. They are confusing and misleading to anyone new to your code base.



I wouldn't call that example nested; I'd call it sequentially chained, and I think that pattern is quite clear even with many conditions in the sequence.

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 agree with the sibling to this comment, though, that a ternary is more readable with newlines and indentation:

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 think your version is reasonably clear, but it won't survive automatic indentation in any tool.

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.


This is a natural point of disagreement. The question is what you're trying to do.

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.


> 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.

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) };
  }


These are great examples, and I totally agree with both of you. However, the code most people write is somewhere in the middle, so it is a more subjective decision.


This I think is obviously personal preference, but to me nested ternarys are pretty readable as long as they're wrapped and indented.

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.


Writing for yourself is a fallacy. We can all read our own code. Write for the next guy who has to look at it and figure out what is going on.


> We can all read our own code

Six-months-ago-myself thought so as well. Turns out six-months-ago-myself is a goddamn moron.


It's also a fallacy that a ternary operator is some impossible to understand concept that will make the code totally unreadable for everyone but you. C programmers should be fine with the use of ternary operators for something like the example shown here.


Who should you be writing for? Everyone is different. What's clearer for one person might be harder to read for someone else.


totally agree, but life is full of compromise. I like to write code so that someone new to field could without too much effort follow what's going on but also so that someone better than I isn't so bored that they miss obvious things in the code (which does happen).

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.


I completely agree with you :-)

But as I say above, I think in certain situations they can be more readable for everyone.


If someone is hired to work on your code base, presumably they know the language. They know ternarys work...

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.


I know ternary operators very well but I still have to think twice when I see them nested

“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.


> 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.


I've been taking the approach of writing code at the highest level of trickery/abstraction that is well-supported by my automated refactoring, debugging, and static analysis tools. This usually means keeping code quite simple. I can then play around with the code much easier if I need to make any changes, and I don't have to focus on the details much because they are quite explicit.

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.


>If someone is hired to work on your code base, presumably they know the language. They know ternarys work...

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).


If people are confused by a ternary statement I don't want them editing code I have to maintain.


If someone thinks people can't or shouldn't be confused by a ternary statement, then I don't want them editing code I have to maintain either...


how many branches/how much depth in a ternary statement would you consider to be too many/too much?

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?


A lisper would say that there is absolutely no limit, and that in fact all programs should be structured like that.


To be clear, a lisper would not solve the problem by switching to a more verbose syntax, but that doesn't mean no depth is too deep. Breaking a big, deeply-nested function up into smaller ones is a perfectly lispy thing to do.



And that's why lispers are at most 1% of the total number of programmers out there, 50+ years after the language was invented.

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 :-)


> You could say that most programmers are crap

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.


Yet it makes people's live better day by day... I feel that as insiders we don't really realize that software works quite well compared to many every day things.


The problem is not knowing how ternaries work, it's about how explicit is that line of code.


Exactly; to use the phrasing used in the presentation, it's about how difficult it is to decode the statements. I'm not a C guy; a regular ternary is fine, as long as it's simple enough. A nested ternary? That gives me all kinds of red flags because I have to lean in and frown and go over it a few times to decode it.


I would make the exception that if you judiciously indent then across multiple lines, to reflect the nested structure the way the if/else equivalent would, then they're fine. But yeah, nested ternaries on one line are impossible to follow.


If you even need to indent them then why you don't write a full if else statement instead? Nested ternaries shouldn't be used at all.


If/else chains are worse because they allow problems like this:

    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.


If I understand correctly, what you're really advocating isn't ternary vs if/else, but expressive conditionals vs imperative conditionals. A statement that is something vs a statement that does something. Using expressions instead of imperative code that mutates state is a staple of functional programming, and is basically always preferred where possible. In some languages you can do this:

  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.


Yes exactly. I mentioned in another comment that rust allows this, and having used it I think the best of all worlds.

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.


True.

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.


For languages in which if/else is unfortunately only imperative, not an expression


IMO if if/else can be used as an expression, your language shouldn't have a ternary operator at all


As someone that would be new to his code base, I neither find that confusing nor am I mislead by it.

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...


Nested ternary is fine as long as it’s written on multiple lines because then it exactly mirrors the if/else-if/else structure.

    return (
        a < b ?
        -1 :
        a > b ?
        1 :
        0)


>then it exactly mirrors the if/else-if/else structure.

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.


They are confusing and misleading to anyone new to your code base.

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?


> 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


No, it doesn't. In most other industries, you are simply considered incompetent if you don't possess the skills or aren't willing to learn the ways of the trade. Look at aviation for example: pilots and ATC talk in jargon, acronyms, and abbreviations like it's a completely different dialect of English, anyone who complains that they can't understand would be laughed at and told to keep learning, and that's the way it should be.

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"


> Look at aviation for example: pilots and ATC talk in jargon, acronyms, and abbreviations like it's a completely different dialect of English, anyone who complains that they can't understand would be laughed at and told to keep learning, and that's the way it should be.

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.


Yes, but the jargon and language used is standardized

...so is the ternary operator.


Did you see that it was me quoting that exact quote as something to argue against?

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.


> I would expect a new hire to be familiar with it, or need time to become familiar with it

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.


I once had to maintain a chat app in which the first developer had written something like the following at some point:

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.


FWIW modern C compilers recommend double-bracing exactly for that reason e.g. in clang, by default, it triggers:

    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) {
                    ^
                    ==


>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?

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".


Yes, they all agree on simplicity. Part of the problem is that simplicity is not entirely objective.

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.


Or it could be that it’s subjective and there is no truly objective answer.


Well, it could be if we had more research.

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".


The mistakes that people make are not subjective. They can detect when they make a mistake, and must do so to correct it. When people make mistakes by using a syntactic construct, that's something can be measured, and people do measure it, which is one of the ways they develop syntax preferences. The problem is that we don't ever measure it in an unbiased and representative manner.


Yes. Go is intended for BIG codebases - millions of lines of code. You can't afford to have to squint and decypher one particular author's code style when you have to go through that amount of code.

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.


Because you can already read your own code. Writing for yourself is bad code. Write for the next person that has to figure it out.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: