36 ms·
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
by illys 7y ago
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 lines
* not obvious (until we read all) that we setup a single variable
* combination of conditions difficult to trace for each given value
* high risk of forgetting cases...
Instead of
myVar =
(cond1) ? 1 : // comment case blabla
(cond2) && (cond3) ? 10 : // comment case bla
(cond2) && (!cond3) ? 100 : // comment case blablabla
...
defaultValue; // comment default
* one line per given value
* operation on a single variable obvious
* match between conditions and values obvious
* high visibility on all the cases
* even more readable with proper vertical alignment
* a comment can be added on each line to document the case
[sorry for the multiple edits: I had a hard time getting { } properly display, they ate up the new lines]
- couchand 7y agoFYI https://news.ycombinator.com/formatdoc https://news.ycombinator.com/formatdoc
- illys 7y agoGreat info, thanks!
- maxxxxx 7y agoI actually prefer the long version. But I think the real trick is to be consistent in style. Parsing ternary operators is a learned skill and so is parsing long if statements. You can get used to both.
- markrages 7y agoProgrammers have to learn to read different styles, to read code from other projects. So use the construction that is clearest in its local context. Style is a matter of taste, just like writing English: there are synonyms and idioms for every idea that you want to express, so use that freedom to choose the clearest communication. Like English, programming code is meant to communicate to other humans, so don't artificially limit your vocabulary in the name of consistency. Consistency is a hobgoblin.
- maxxxxx 7y agoI don’t think consistency is a hobgoblin (I hope I understand the meaning correctly). When you are new to a project you should follow the style of what’s already there and not do something completely different. Obviously there may be good reason but that should be discussed and you also should ask yourself if you are just not willing to adapt or if you are actually making things better.
- oblio 7y ago> Consistency is a hobgoblin. A foolish consistency is the hobgoblin of little minds. The whole quote is important. Cutting parts out changes the meaning of the whole thing.
- deng 7y ago> Combinations of if/else can seriously damage readability and maintainability. Yes, that's why for selections we usually use switch/case. But of course every programmer worth his money just knows that ?: is right associative and that '&&' is on precedence level 11 while '?:' is on 13. If that's not "clever code", I don't know what is...
- illys 7y agoNever thought the ternary operator required high expertise for associativity/precedence reasons and could sentence me to "clever code"... Besides switch/case do not apply to a mix of true/false conditions but to the different values of a single variable. Funny :-) Thanks.
- deng 7y ago> Never thought the ternary operator required high expertise for associativity/precedence reasons If you chain or nest them. > Funny :-) I'm glad I could brighten your day a little bit.
- jlokier 7y ago> 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 'if..else' as right associative, but it actually is, and everyone uses that fact without thinking about it. Same for '?:'. The associativity is just a formality that nobody thinks about. > '&&' is on precedence level 11 while '?:' is on 13 On this I agree. I routinely put parentheses around complex conditions on the left side of ternaries for this reason. I do remember the precedence, but I think leaving the parentheses out can raise a little doubt in the reader's mind, because it is common to use logical operators for control flow in languages like JavaScript, and control flow is at the same conceptual level as '?:'.
- 7y ago
- jacobush 7y agoOr break them out into a function, then you can "short-circuit" with a return: int logic() { if (cond1) { return 1; } if (cond2) { if (cond3) { return 10; } return 100; } // ... return defaultValue; } Since it's a function, it's also apparent that you are only touching one variable: myVar = logic(); I love the ternary operator, but I find it's harder to step it in a debugger though.
- spectramax 7y agoShort circuiting is poor for readability in my view because not only do you have to parse the syntax but you need to mentally walk through each case to understand what’s going on. Traditional if/else conditionals express clarity by embedding the decision for myVar and visually show how it would be assigned in various cases. As @hotBacteria said: if(cond1){ myVar = 1; } else if(cond2 && cond3){ myVar = 10; } else if(cond2 && !cond3){ myVar = 100; } else { myVar = 4; } // Now return myVar return myVar; I’ll take this syntax over short circuiting any day.
- drawkbox 7y agoClear conditionals like this (or short circuiting) are very nice when it comes to merging. One liners and mammoth tertiaries that are too long can lead to conflicts and more problematic merges/diffs/etc. One operation per line makes repos and merging, as well as blaming, reading and reviewing, easier to parse.
- hotBacteria 7y agoYou can write it without resorting to one-line conditionals: if(cond1){ myVar = 1; } else if(cond2 && cond3){ myVar = 10; } else if(cond2 && !cond3){ myVar = 100; } else { myVar = 4; } But when I encounter this kind of situations I have other problems than code formatting anyway: with 3 conditions you have 2^3 possibilities to check: are you really really sure (!cond1 && !cond2 && cond3) should give you defaultValue ? Do you really want 1 even if !cond2 ? etc... When possible these conditions should be avoided in the first place (depending on context of course)
- 5trokerac3 7y agoIf you're running through that many conditionals, it may be clearer to work them into a state enum that you can use with a switch.
- apta 7y agoGolang doesn't have enums
- 5trokerac3 7y agoNeither does JS, but there are ways to accomplish the same thing[0]. [0] https://stackoverflow.com/questions/14426366/what-is-an-idiomatic-way-of-representing-enums-in-go https://stackoverflow.com/questions/14426366/what-is-an-idio...
- tabtab 7y agoThat makes code formatting inconsistent because many prefer the other way. The VB.Net style is more compact and readable in my opinion: if cond1 then myVar = 1 else if cond2 and cond3 then myVar = 10 else if cond2 and not cond3 then myVar = 100 else myVar = 4 end if The VB.Net style also makes it easier to identify mismatched blocks because the "enders" are named differently. (One can also put the assignment on the same line as the conditional, but I generally don't recommend it.) Some may complain it's slightly verbose, but few if any will claim it's not clear.
- codeulike 7y agoI much prefer the if/else, thanks very much.
- datavirtue 7y agoIt doesn't matter what you use. If I can't understand your code quickly I'm going t fail your code review. The use of if/else versus ternary operators is a matter of taste. If your composite of fucntions/methods reads declaritivly then the code will be easy to understand and your I dividual methods should also be small and easy to grok.
- pier25 7y agoPersonally 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.
- Const-me 7y agoI usually write if in such cases. Or sometimes for multiple condition branches I use bitwise operators to create integer bit mask with couple of bits, then switch(), or array indexer, with all possible 4-8-16 values. Your version is more error prone because contains more code than necessary. Note how it’s just “else” in original version, and manual negate statement, (cond2) && (!cond3), in your version. Easy to screw up when modifying the code at some later point. When you then need to replace cond3 with cond4, forget about second branch and the algorithm will break.
- Schattenbaer 7y agoI have also seen switch statements used (abused?) to do a similar thing. Since a switch executes the first statement that is equivalent to the control expression you can pass true as the control expression, e.g.: switch(true) { cond1: return 1; cond2 && cond3: return 10; //etc } Not sure I'd recommend it but it works in a few languages :) Elixir has cond that does this nicely without resorting to strange switch statements: https://elixir-lang.org/getting-started/case-cond-and-if.html#cond https://elixir-lang.org/getting-started/case-cond-and-if.htm...
- networkimprov 7y agoWhat we should be able to write in Go, but can't because the compiler (wrongly) imposes formatting: if a { v = 1 } else if b && c { v = 10 } else if b && !c { v = 100 } else { v = d }
- atq2119 7y agoNo. This kind of alignment is basically always a mistake and hurts software maintainability in the long run. The problem is that if one of the conditions changes, for example due to a variable renaming, you likely have to change the alignment on all lines. Aside from being very tedious even if you're a lone coder, this creates noise in diffs that makes automatic merges fail much more frequently.
- Cthulhu_ 7y agoI strongly disagree; creative formatting like this means that as a reader, you have to squint and readjust to this new style to see what is going on. I mean, this whole thread reads like a holy war about code style - something the Go developers EXPLICITLY want to avoid because discussions about code style are a waste of time. Everyone reading the above segment of code will have a different opinion on how to format it, while the real question should be "What does this code do". I mean this is highlighted in the preface of the presentation: "When you or I say that a foreign codebase is unreadable, what I think what we really mean is, I don’t understand it." When I read this code I don't understand it, and it's not because of the formatting per se. Playing with the formatting does not make the purpose of the code clearer. This whole thread where people argue about how to best format a structure is missing the point of the article completely.
- GorgeRonde 7y ago> When I read this code I don't understand it ?
- deleted 7y ago[deleted]
- MaxBarraclough 7y agoI guess I'm in a minority here, but I wish more non-functional languages would feature pattern matching, of the sort seen in Haskell and OCaml. [0] This language-design question is essentially a solved problem, but few new languages (outside the pure-ish functional ones) make the effort. Trivial example in OCaml: let imply v = match v with (true,x) -> x | (false,x) -> true;; [0] https://caml.inria.fr/pub/docs/oreilly-book/html/book-ora016.html https://caml.inria.fr/pub/docs/oreilly-book/html/book-ora016...
- rbavocadotree 7y agoIt's becoming more popular. Kotlin, Swift and now C# 8.0 have pattern matching (well, close enough at least). I sure hope other languages take note, because you are right about it essentially being a solved problem.
- commandlinefan 7y agoScala, too.
- sk5t 7y agoYep! Scala's pattern matching is so far ahead of Kotlin's, vis a vis destructuring, that it strains the conscience to say Kotlin has pattern matching at all. FWIW I find both to be enjoyable languages in which to work.
- Roboprog 7y agoAbout the only change I would make to your layout is to put a line break after the “?”, with the value sub-indented on the next line, to avoid “guessing” /maintaining how far to tab over the value column. Thus, it would be layed out like an else-if chain, but without the extra verbiage, particularly the repeated assignment. (I can’t put in a proper example from my iPad, as it wants to capitalize all the lines, etc) Disclaimer: Ruby was very influential to me in the mid 2000s, even if I never wrote any for pay. The if (else-if...) statement in Ruby works like a ternary chain in C based languages.
- franciscop 7y agoI prefer to put it into a function and do early return/guard clauses for complex conditionals: const pick = (param1, ...) => { if (cond1) return 1; if (cond2) return 10; return 100; };
- guelo 7y agoOr if(cond1) myVar = 1; else if(cond2 && cond3) myVar = 10; else if(cond2 && !cond3) myVar = 100; else myVar = 4;
- psychoslave 7y agoenum situation { boring, clear, cleaver } selector(context) if context.proves(cond1) return cleaver if context.proves(cond2) return clear return boring adjudicator(context) switch( selector(context) ) cleaver: make_something_cleaver() clear: make_something_clear() default: make_something_stupid_simple() Not completely the same situation, though, as there are no assignation here. For simple cases (`v = e0 ? e1 : e2`), ternary operator sure is fine, but for more complex cases, `some_var = selector(context)` tend to be a clearer path. That will also be better rendered in your API as the function fine documentation will have more chance to be extracted properly.