Live data from Hacker News

Clear is better than clever [pdf]

dave.cheney.net

121–130 of 335 posts

Re: Clear is better than clever [pdf]

#121
post #33

Sometimes clever means succinct. And maintaining less lines of code can also lead to less bugs.

I'm not disagreeing with you, and maybe this is just my personal experience, but there seems to be a movement that promotes succinctness over all other qualifications. It's as if someone's comp-sci class had a competition for who could accomplish some task with the fewest bytes of code and a whole generation of programmers never got out of that mode. I've heard comedians talk about a set not having any fat in it... e…

On the contrary, I think the zeitgeist for the last 5-10ish years has been the opposite. "Verbose and explicit > Succinct and implicit".

I think python and golang are largely responsible for this kind of movement in recent years. Because of that, I find languages like Perl 6 to be a breath of fresh air.

Re: Clear is better than clever [pdf]

#122
post #39

It's interesting that the comparator function is one of the examples, because that's something which shows how people often confuse verbose with clear ; I think it's best written in a single line: return a b ? 1 : 0; That's one line, compared with the 10(!) of his proposed method using a switch statement. Having worked with some "modern" codebases, I think verbosity is a bigger problem that's become especially preval…

Fully agree! Combinations of if/else can seriously damage readability and maintainability. I (all the time) face verbose code that pretend to be readable and that requires lots of attention just to find out you are setting a single variable across 10-20 lines. Example: if (cond1) { myVar = 1; } else if (cond2) { if (cond3) { myVar = 10; } else { myVar = 100; } ... } else { myVar = defaultValue; } * huge number of lin…

Personally when I find myself with these constructs of complicated and nested conditions I try to simplify the branches and condition checking to functions.

This is not always possible, but when it is it's much easier to follow the flow of code and it reduces the number of lines of the condition tree.

Re: Clear is better than clever [pdf]

#124
post #67
post #53

Nice intro, which then jumps to an absolutely arbitrary (for someone who hasn't coded in Go) set of recommendations, which are (imo) poorly argued from the perspective of clarity. Especially the case of "var person int", how is it cleaner than "var person int = 0" - and why are we even talking about zero values when the variable was not supposed to be intialized. Many decent imperative languages let you not intiializ…

'Especially the case of "var person int", how is it cleaner than "var person int = 0"' Because they're equivalent. There no uninitialized values in Go; everything not given an explicit initializer is zeroed out. "So are we writing code to be read by Go experts? Go noobs? Software Engineers with general background in many lanaguages, but perhaps not go? Noob programmers?" One of the nice things about Go not particular…

> > Especially the case of "var person int", how is it cleaner than "var person int = 0"'

> Because they're equivalent. There no uninitialized values in Go; everything not given an explicit initializer is zeroed out.

Using equivalence is often regarded as less clean.

Because it relies on the reader to be familiar with language-specific quirks. Even if they are, it adds a touch of cognitive overhead until it's become habitual.

In a language like Go, I think the performance will be identical in both cases because it has a basic data flow optimiser.

I program a lot in Perl5 too, and like you, I tend to favour the built-in support for things. I also really recommend the "experimental" function signatures, which make it read a lot more like other languages, and removes boilerplate. However, for better or worse, performance matters in some things I write, and Perl5's optimiser doesn't bother with much dataflow optimisation (improving the interpreter seems to have stalled for decades). So when performance matters, I do write things like 'my $x;' instead of 'my $x = undef;', relying on familiar equivalences. I would prefer the interpreter made them identical so I could state the intent more clearly without penalty, especially because performance-sensitive code tends to be difficult algorithms where clarity is more important.

Re: Clear is better than clever [pdf]

#125

It's interesting that the comparator function is one of the examples, because that's something which shows how people often confuse verbose with clear ; I think it's best written in a single line: return a b ? 1 : 0; That's one line, compared with the 10(!) of his proposed method using a switch statement. Having worked with some "modern" codebases, I think verbosity is a bigger problem that's become especially preval…

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

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

Re: Clear is better than clever [pdf]

#126
post #113

Earlier quoted context omitted.

What's the point? Why not just make it an if-elseif-else block and make it obvious?

Ternary isn't the same as a branch and you want your this simple comparison function to get inlined every time

> Ternary isn't the same as a branch

It should be. The C ternary is just an expression-oriented version of if/elseif/else.

Languages with a functional bend simply make if/elseif/else an expression in the first place e.g. in Rust it'd be

      return 
        if a  b { Ordering::Greater }
        else { Ordering::Equal }
Though obviously that specific version is an overly complex way of writing:

    return a.cmp(b);

Re: Clear is better than clever [pdf]

#127

It's interesting that the comparator function is one of the examples, because that's something which shows how people often confuse verbose with clear ; I think it's best written in a single line: return a b ? 1 : 0; That's one line, compared with the 10(!) of his proposed method using a switch statement. Having worked with some "modern" codebases, I think verbosity is a bigger problem that's become especially preval…

I always feel like the idea of "clear" vs. "clever" is missing the point.

When I've come across - or written - "clever" code in my career, it's always been about making something that normally wouldn't fit into memory - or perform slowly - viable. The examples of this are numerous and still happen plenty today on modern hardware (e.g. games, VMs, NaN tagging). There is nothing wrong with "clever" code so long as it's _clearly_ documented and abstracted away with various types, macros, functions, etc.

Then there's all other code. And what I feel the author wanted to say was that anyone coming along later shouldn't have to think twice about what a given line of code is doing.

If there's a bug, sometimes I'll be lucky enough to have a debugger, sometimes not. But, I shouldn't have to second guess a line of code to determine whether or not the bug is there. In your nested ternary expression, I would be second guessing all over the place; what's the precedence? Hell, whenever I use ternary operators I still force myself to parenthesize the conditional, because while operator precedence in C/C++ is well defined, it's not well known.

Clarity is another reason I prefer type safe languages over dynamic ones for critical code. It allows me to be a little more "expressive" with my code where the reader coming along after-the-fact can be assured that everything works out type-wise.

Having worked on code that people's lives depended on, there are times when getting it wrong isn't an option. I can only feel for the software engineers of the 737 MAX going back over gobs of code and trying to dissect what went wrong and come up with a fix. They'd be heavily scrutinizing every line of code, and I can promise that a nested ternary would be very scrutinized. But, so would a deep chain of if, elsif, elsif, ... and recursive functions (which are often a _big_ no-no in mission-critical code). Although, I don't think anyone here effectively argue that recursion is something to be considered "clever".

It's all about a code reader being able to _prove_ to themselves that the code does _exactly_ what it looks like it does and nothing more.

I'm not a Rust fanboy, but - just like static type checking - the borrow checker adds one more level of surety to the reader that another thread isn't coming along and stomping over the data. And this is without needing the whole context of the code base. This is a _big_ deal.

Re: Clear is better than clever [pdf]

#128
post #44
post #24

Earlier quoted context omitted.

Focusing on readability of the code and putting "machine execution" second, means putting the user experience second Nothing kills a users experience like buggy code. Nothing contributes more to buggy code than unreadable or unclear code. This is why another proverb often quoted is "First make it correct, then make it fast". Fast but buggy code will lose to slower but correct code everytime.

> Fast but buggy code will lose to slower but correct code everytime. This is unfortunately not always true. It depends on what the consequences for incorrectness are. Outside of enterprise and the web, fast code can be an important selling point, and it may end up being prioritized over correctness, as long as the result is "correct enough". I wish it weren't so.

Something being correct enough is another way of saying "Most if not all of our users will never experience this." That's essentially saying that the code is not buggy.

Now if your code is such that most of your users frequently experience incorrect behavior then I think you'll discover that you'll lose to a competitor.

Re: Clear is better than clever [pdf]

#129

Earlier quoted context omitted.

> every programmer worth his money just knows that ?: is right associative I would hope every programmer worth their money are very comfortable with it, because chaining like this is a very common syntactic pattern in other people's code: cond1 ? val1 : cond2 ? val2 : cond3 ? val3 : other It is made less readable if you turn it into a right-nested mess of parentheses, so that is rarely seen. Just as nobody thinks of…

> because chaining like this is a very common syntactic pattern in other people's code If it's very common where you work, then run, don't walk, away...

No, it's very common in code people will encounter if they read a wide range of other people's code outside work.

If you haven't encountered it often enough that it's familiar, than I think you probably don't read much code outside a small bubble.

Re: Clear is better than clever [pdf]

#130
post #79

Earlier quoted context omitted.

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 thi…

> 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:
                    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...
Post reply on HN