TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Anti-if: The Missing Patterns

130 pointsby joejagalmost 9 years ago

24 comments

userbinatoralmost 9 years ago
I&#x27;m disappointed that of all the patterns presented, most of them actually increase the complexity to some extent, and none of them are the use of a lookup table&#x2F;array. IMHO that is the &quot;real anti-if pattern&quot; here, and it can <i>immensely</i> simplify code. I&#x27;ve taken long, convoluted nested if&#x2F;else chains with plenty of duplication and turned them into a single array lookup. A bonus is that performance and size often benefit as well, since the code is far less branchy.<p><a href="http:&#x2F;&#x2F;www.codeproject.com&#x2F;Articles&#x2F;42732&#x2F;Table-driven-Approach" rel="nofollow">http:&#x2F;&#x2F;www.codeproject.com&#x2F;Articles&#x2F;42732&#x2F;Table-driven-Appro...</a>
评论 #11874434 未加载
评论 #11877380 未加载
评论 #11874658 未加载
wtetzneralmost 9 years ago
&gt; Context: You have a method that takes a boolean which alters its behaviour<p>&gt; Problem: Any time you see this you actually have two methods bundled into one. That boolean represents an opportunity to name a concept in your code.<p>This is only true if you&#x27;re using literal booleans at you call sites. If the booleans are coming from somewhere else, like user input, you just moved your single if statement in the method out to every call site.<p>&gt; Context: You are calling some other code, but you aren’t sure if the happy path will suceceed.<p>&gt; Problem: These sort of if statements multiply each time you deal with the same object or data structure. They have a hidden coupling where ‘null’ means someting. Other objects may return other magic values that mean no result.<p>Passing a default value works, but in Java 8, returning an Optional is cleaner. And if you wanted a default value, you can still do<p><pre><code> repository.getRecord(123).orElse(&quot;Not found&quot;); </code></pre> Edit: Also, the coping strategy link [1] he gave to remove exceptions sounds a lot like restarts in Commons Lisp&#x27;s condition system.<p>[1] <a href="https:&#x2F;&#x2F;silkandspinach.net&#x2F;2014&#x2F;11&#x2F;06&#x2F;on-paperboys-newsagents-and-exceptions&#x2F;" rel="nofollow">https:&#x2F;&#x2F;silkandspinach.net&#x2F;2014&#x2F;11&#x2F;06&#x2F;on-paperboys-newsagent...</a>
评论 #11874196 未加载
评论 #11873659 未加载
评论 #11875582 未加载
评论 #11873542 未加载
tgbalmost 9 years ago
I&#x27;m afraid the article has left me unconvinced. I&#x27;m open to having my mind changed on the matter, though.<p>The point of passing boolean params instead of named functions is that most of the time there is shared code between the two paths and not literally all the code is enclosed in either the if or the else block. If the author was intending just to restrict to that one specific case where there was no overlap whatsoever, then I guess I would agree but I&#x27;m not sure how often that happens.<p>Polymorphism has some advantages but it&#x27;s certainly got it&#x27;s own troubles in terms of understand-ability. The author links to an article which even states &quot;I think that we hate conditionals more than we should. Introducing polymorphism too soon will complicate your code, making it hard to understand and test.&quot; It then goes on to give an example where it says polymorphism is actually the right choice, to avoid two switches. I&#x27;d say that at least this trivial example would be best handled by some data-driven approach. Look up salaries in a database instead.<p>The inline statement one can certainly be better in some cases, but the example given is quite poor. What does &quot;foo &amp;&amp; bar || baz&quot; mean? Well, now I have to remember the order of operations ... &#x27;or&#x27; after &#x27;and&#x27; I guess. Surely this is easier to misread than the if statement example, which in my mind closely matches how I think. At least it should be &quot;(foo &amp;&amp; bar) || baz&quot;. But even then, that only works when the actual return type is boolean and there are no state changes made.<p>I don&#x27;t really see how using an Optional type would remove the if(null) checks. It just makes the meaning explicit, which is good but not remedying the original problem.<p>5 seems nice.
评论 #11874502 未加载
评论 #11874176 未加载
评论 #11876115 未加载
评论 #11876607 未加载
akkartikalmost 9 years ago
This recent article seems relevant: <a href="http:&#x2F;&#x2F;www.sandimetz.com&#x2F;blog&#x2F;2016&#x2F;6&#x2F;9&#x2F;make-everything-the-same" rel="nofollow">http:&#x2F;&#x2F;www.sandimetz.com&#x2F;blog&#x2F;2016&#x2F;6&#x2F;9&#x2F;make-everything-the-s...</a>
评论 #11874230 未加载
yildirimalmost 9 years ago
I had a professor in university who was frequently saying he developed a thousand application and he didn&#x27;t use any if.<p>I know it&#x27;s extreme but is there any way to reduce (lets say 5 to 1) conditions?<p>I think he was developing Fortran apps.<p>Anecdote: This very same professor asked us to do a matrix operation (I don&#x27;t remember what) without using if once. He said it would be faster than using ifs. Many of us couldn&#x27;t. Then he revealed his solution, he was getting first indice of first row and doing some operation and getting the last indice of last row, then second indice of first row and so on. One of my friend told that if he wrote the algorithm plain using ifs, it would be faster. Professor told it&#x27;s impossible.<p>In the end of the day the version which was written using ifs was a lot faster than the version without ifs. Because of the changing indices cause a lot of cache misses but getting matrix elements sequentially used cache correctly.
评论 #11874865 未加载
评论 #11875860 未加载
catnaroekalmost 9 years ago
All of the proposed solutions are deficient:<p>&gt; Pattern 1: Boolean Params<p>This solution suffers from what we typeful programmers call “Boolean blindness”: <a href="https:&#x2F;&#x2F;existentialtype.wordpress.com&#x2F;2011&#x2F;03&#x2F;15&#x2F;boolean-blindness&#x2F;" rel="nofollow">https:&#x2F;&#x2F;existentialtype.wordpress.com&#x2F;2011&#x2F;03&#x2F;15&#x2F;boolean-bli...</a> . When you compute a bit, what you are actually interested in is the <i>meaning</i> of the bit, which isn&#x27;t included in the bit itself. For example, if the bit is set, then “x” is less or equal than “y”; if it&#x27;s unset, then “x” is greater than “y”.<p>&gt; Pattern 2: Switch to Polymorphism<p>Dynamic dispatch (what the author wrongly calls “polymorphism”) can be used for <i>open-ended</i> case analysis. But it suffers from two drawbacks: (0) Unless you have multimethods, case-analyzing two or more values at the same time is a bitch! (1) Even if you do have multimethods, the open-ended nature of this whole business makes static exhaustiveness checking (making sure that no case is missing) impossible.<p>&gt; Patte[r]n 3: NullObject&#x2F;Optional over null passing<p>Null objects are still falling back to pattern 2, and optionals are severely underpowered in languages without pattern matching and compile-time exhaustiveness checks. For instance, one of my favorite patterns is implementing operations (say, “merge”) on non-empty collections, then extending them to possibly empty ones:<p><pre><code> (* pe = possibly empty *) datatype &#x27;a pe = Empty | Cons of &#x27;a (* extend a merge operation on non-empty collections * to possibly empty ones *) fun pointed _ (xs, Empty) = xs | pointed _ (Empty, ys) = ys | pointed op++ (Cons xs, Cons ys) = Cons (xs ++ ys) fun merge (xs, ys) = ... (* possibly complicated *) and mergePE args = pointed merge args </code></pre> How do Java-style optionals help?<p>&gt; Patte[r]n 4: Inline statements into expressions<p>This only makes the problems associated to Boolean blindness even worse.<p>&gt; Pattern 5: Give a coping strategy<p>What if there is no sensible default value? Just... no.<p>---<p>What you really need is algebraic data types, pattern matching and exhaustiveness checks.
jlg23almost 9 years ago
Uhm. No, no, no, no and no.<p>&gt; [Pattern 1] Solution: Split the method into two new methods. Voilà, the if is gone.<p>And so is parametrization.<p>&gt; [Pattern 2]: Solution: Use Polymorphism. Anyone introducing a new type cannot forget to add the associated behaviour.<p>OTOH you make extensions more complex. I usually write in lisp and DO NOT use polymorphism for this but etypecase in the base implementation of a method (switch&#x2F;case where the default case automatically throws an error). This allows people who extend my code to implemented a method specific to a type that my base implementation handles and thereby overriding my implementation with minimal effort.<p>&gt; [Pattern 3]: Solution: Use a NullObject or Optional type instead of ever passing a null. An empty collection is a great alternative.<p>This is, IMHO, not relevant at all - the decision how null values are handled simply has to be documented so programmers can make an informed choice. The only exception I accept is an empty collection which I&#x27;ll always prefer over null, assuming that all other code handles empty collections gracefully.<p>&gt; [Pattern 4]: Solution: Simplify the if statements into a single expression.<p>Works for the contrived example (and we all know that the code is really bad). For more complex code I rather follow the logic line by line instead of mentally disassembling a 10 line boolean expression.<p>&gt; [Pattern 5]: Solution: Give the code being called a coping strategy.<p>No, just no. One does not aim at removing exceptions. Those are to be handled by a higher layer who actually knows how to handle it. If one uses default values one still has to check on that layer with the added burden of using a default value that is guaranteed to never be returned as a real value so we can make the distinction between record found&#x2F;record not found.
efraimalmost 9 years ago
Misko Hevery did a Google Tech Talk about this some years ago, he also use a very similar example with switching on the birds.<p><a href="https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=4F72VULWFvc" rel="nofollow">https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=4F72VULWFvc</a>
评论 #11876551 未加载
michaelmioralmost 9 years ago
One thing I&#x27;ve always done with if statements is when I have an if with a negated condition and no else case<p><pre><code> if (!condition) { &#x2F;&#x2F; do stuff } </code></pre> When I want to add an else case, I swap the order so the condition is no longer negated.<p><pre><code> if (condition) { &#x2F;&#x2F; do other stuff } else { &#x2F;&#x2F; do stuff } </code></pre> To me it&#x27;s easier to read a non-negated condition and the else case then feels more natural. (It avoids having to internally think about else case as &quot;condition not not satisfied&quot;.)
评论 #11874667 未加载
praptakalmost 9 years ago
Using &#x27;if&#x27; indicates programmer&#x27;s doubts about what a program should do. These are usually rooted in low self esteem and troubled relationships with parents.
评论 #11875499 未加载
ankurdhamaalmost 9 years ago
Getting rid of if&#x2F;else is nothing but moving the if&#x2F;else to somewhere else.
评论 #11874440 未加载
dfoxalmost 9 years ago
If you want extreme case of removing all ifs, see how ifTrue: and friends are implemented in Smalltalk (ie. true and false are instances of different classes), although essentially all compiler implementations turn that into normal conditional branches in generated bytecode.
评论 #11875366 未加载
评论 #11877108 未加载
mikkomalmost 9 years ago
All these complicated examples and no mention of functional pattern matching or pattern matching at all.<p>IMHO These are by far the two most important ways to get rid of complex code embedded with ifs.<p>One very good example of this is erlangs factorial method.<p><pre><code> factorial(0) -&gt; 1; factorial(N) -&gt; N * factorial(N-1). </code></pre> There are many other nice examples at the erlang documentation:<p><a href="https:&#x2F;&#x2F;www.erlang.org&#x2F;course&#x2F;sequential-programming#funcsyntax" rel="nofollow">https:&#x2F;&#x2F;www.erlang.org&#x2F;course&#x2F;sequential-programming#funcsyn...</a>
评论 #11875351 未加载
评论 #11875135 未加载
skybrianalmost 9 years ago
Generally speaking, for every refactoring you could do, the opposite move is sometimes useful too. For example, inlining a function and extracting a helper function could both be a way to simplify code, depending on what you&#x27;re trying to do.<p>Similarly here, I think it&#x27;s good to know how to get rid of an if statement, but keep in mind that sometimes you might want to introduce an if statement. Sometimes you want polymorphism and sometimes it&#x27;s better to do the same thing as pattern matching.
zimbatmalmost 9 years ago
The first &quot;anti-if&quot; should be to resist adding unnecessary features, on every level. The less branches, the less possible ways to fail.
Illniyaralmost 9 years ago
I like the addition of the &quot;tolerance&quot; bit to examples, more blogs should have a &quot;when not to use&quot; section.
xamuelalmost 9 years ago
I don&#x27;t know what&#x27;s so &quot;Missing&quot; about the polymorphism anti-if: I&#x27;ve seen it ruin plenty of code.
a3nalmost 9 years ago
&gt; Patten 3: NullObject&#x2F;Optional over null passing<p>The solution here disperses the responsibility of keeping sumOf safe to its callers, all over the place, instead of a single location in sumOf.
评论 #11874048 未加载
keenerdalmost 9 years ago
This reminds me of something I read on HN a while back and forgot to bookmark. (Naturally I&#x27;ve been unable to find it since.)<p>I think the article was written by a company who made a static code analysis tool. They described how there wasn&#x27;t a large difference in quality between FOSS and proprietary code, except for one detail: FOSS code used &quot;else&quot; statements much less.<p>Anyone have any idea what I&#x27;m thinking of or if I&#x27;m even remembering it correctly?
评论 #11875497 未加载
评论 #11875141 未加载
salex89almost 9 years ago
And if there is no record in the repository, what should I do with the default value? Check if the returned value is different from the default? I actually don&#x27;t see any real benefit in cleanliness. The only thing that could actually happen is using the default in some way that could eventually bring the data in some strange state. An NPE would at least shout &quot;YOU MESSED SOMETHING UP&quot; in my face.
glastraalmost 9 years ago
Pattern 5 is all about removing semantic value from null references, but has nothing to do with if statements... In fact, the if-else remains (albeit hidden because of an early return inside an if, which is even worse in my opinion).
jduvalmost 9 years ago
I&#x27;m surprised there was no mention of the Specification pattern.<p><a href="https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Specification_pattern" rel="nofollow">https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Specification_pattern</a>
评论 #11876205 未加载
duke360almost 9 years ago
I&#x27;m quite supportive to the anti-if pattern but the presented example sounds me more like &quot;code smells&quot; and doesn&#x27;t really makes me happy to me anti-if is a great winner when you can safely compute all the execution path in advance (in a way where you have not to nest ifs, of course!) but in situations where your code have to react to something that happens &quot;on the moment&quot;... well, probably &quot;if&quot; are more convenient (not always of course) anyway thanks to have raised the point, this topic is always interesting to me
ameliusalmost 9 years ago
I was hoping for some examples that reduce costs incurred by failing branch-prediction in the CPU.