7 ms·
IF-less programming
- Zak 14y agoIt's not just an OO thing. If you're using conditionals, you might actually want something else - a dispatch table, for example. Thinking about alternatives to conditionals will probably result in better code most of the time, but actually trying to go if-less seems forced.
- andrewcooke 14y agohow does "try to use less X because Y" become "don't use X"? and why is this considered good? to clarify: my question "why is this considered good?" isn't about "if-less programming", but about taking ideas to dumb extremes.
- alisnic 14y agoYou did not read the article, by no means I suggest to get rid of the IFs completely.
- laurent123456 14y agoIn general, this kind of don't-use-this patterns are not very constructive. It reminds of this obsession of not having global objects at all cost. And we end up with singletons, static classes, etc. that are all just global objects in disguise. If I need a global, I use a global and call it that, same if I need an if/else statement. It's an interesting article though, especially the conclusion: "Any language construct must be used when it is the more reasonable thing to do. I like the fact that the presence of if-s can indicate a bad OO design."
- DigitalJack 14y agoI think your quote sums up the intention of this article and most of the "don't do this" posts. What they all are getting at I think is that constructs in a language can be "abused" or used less than effectively either in terms of processing efficiency or user/programmer interaction. So when they say "don't do this" it's more to show cases where doing whatever "this" is can be bad. Not that it's always bad and should never be used. Also, the "don't do this" mentality can be a useful exercise to teach yourself alternative ways of getting things done.
- d--b 14y agoFine, but the point is that the guy is only half-way through his reflexion. OO programming is an effective way to organize the business logic. And that logic should be coded in a sequential fashion to be readable and maintainable. Finding the right balance would be a much more interesting subject than to say 'ifs suck'
- jre 14y agoThis is an interesting idea and I think it's interesting to have it somewhere in mind while programming. A few thought though : - If you really push this to the extreme, you'll end up with all the logic hidden in the classes inheritance hierarchy. I'm not sure this is more readable/extensible than if/else statements. - Most of the example given by the author to use "language features" are just syntactic sugar. Using collection.select instead of collection.each or || instead of if else is really just a matter of notation. I doesn't reduce the number of test cases required for your code and it might lead to "magical" one-liners that you have to read 20 times to understand.
- d--b 14y agoNo! This practice is absolutely terrible! Object-oriented programming _can_ allow you to make if-free programs, but it will cost you readability/maintainability. Many people have been fighting against the overuse of inheritance and oo patterns that emerged in the 90s. Do not fall in the trap again!
- nikic 14y agoI think this is (once again) just a question of finding the right balance. Using polymorphism to avoid manual conditionals is a practice that can often improve readability and maintainability. One just shouldn't drive it too far. I do agree that people are massively overusing inheritance. In particular its often used as a means for code reuse, not for polymorphism. In most cases composition should be favored over inheritance.
- d--b 14y agoCouldn't agree more.
- michaelfeathers 14y agoI don't advocate a completely if-less style, but I do often grep and count ifs and elses in a code base before digging in, just to get a sense of what sort of developers have been there. I think the if-less style is great kata material. You learn a lot when you apply it on a toy project rather than a real one. Sometimes you need to overdo things in order to understand what their limitations are.
- davidw 14y agoFor those who have never used a functional programming language, those often allow you to do "if-less" or at the very least "if-lite" programming via pattern matching.
- ufo 14y agoThe thing with pattern matching is that it is as safe as the OO approach, in the sense that the compiler will warn you if you don't match all the cases and that forcing you to use an explicit ADT protects against boolean blindness (for example, writing code that assumes you are in case 2 if you are not in case 1) That said, when it comes to extensibility, pattern matching is more similar to if statements then the OO - its easy to write new functions but hard to extend the original ADT with new cases. If being able to add new functions is important it might be better to use if statements then to do a major rewrite to use OO instead. (You could always use a visitor pattern if you want the extra compiler safety but that can get very complicated, IMO)
- CJefferson 14y agoIs pattern matching really that different from switch statements, which are really just fancy if statements? Trying not to sound sarcastic, but if people are for if-free programming, pattern matching does not seem to be the answer for me. When I add a new type in haskell, I usually find myself having to look through all my pattern matchings.
- riffraff 14y agoI second the question, pattern matching is cool for destructuring or for completeness checking (not sure those are fundamental properties of pattern matching though), but it does not solve the problem of adding new behaviour without changing existing code any more than an `if` does.
- ufo 14y agoThis is not a bug - its a feature! Pattern matching/ifs are good for creating new functions, while OO style makes it hard to add new methods.
- Strilanc 14y agoReplacing a switch statement or large if statement with a virtual call (enum -> interface refactoring) can definitely be very beneficial. It can turn [crimes against humanity](https://github.com/aidanf/Brilltag/blob/master/Tagger_Code/final-state-tagger.c#L145 https://github.com/aidanf/Brilltag/blob/master/Tagger_Code/f...) into perfectly respectable code. But, obviously, don't take this too far. If you find yourself not using an if statement (or ternary operator) when writing the absolute value (for non-optimization reasons)... you've gone too far. int UnclearAbs(int value) { return value * (value >> 31); }
- lmm 14y agoAbuse of bitwise operators is an abomination, but I'd instinctively write "value.abs" (scala), which is clearer than an if. Obviously many things are ultimately implemented using if, but it's too low-level a construct to be using for day-to-day work.
- tallanvor 14y agoSome of this seems petty. For example, the author gives the following "bad" example: def method (object) if object.property param = object.property else param = default_param end end And suggests that this is better: def method (object) param = object.property || default_param end To me, this is an example of an if statement by another name. Sure, you cut out a few lines, but it's still an if-then construct. Both examples are most likely going to be treated the same way by the compiler as well.
- Adirael 14y agoI agree, and I prefer the first one, it just seems clearer for me.
- michaelfeathers 14y agoI disagree. Sure, the code is functionally equivalent today, but the next person touching the 'or' version might be less likely to introduce side-effecting computation. The 'if-else' version is completely open-ended. We need to think about the effect our choices have on the next person who touches the code. Often with a different baseline people make different changes. It's a micro-form of 'No Broken Windows'.
- delinka 14y ago"We need to think about the effect our choices have on the next person who touches the code." And that next person might be the newbie on the team that doesn't have the business domain knowledge built up over years of working for This Company. So I'll use the if statement to keep intent clear. Or maybe it'll be someone whose primary programming language doesn't use the || operator. Or maybe, for some unknown reason, it'll be a PHB. So I'll use the if statement to keep intent clear.
- michaelfeathers 14y agoSetting the baseline gives you a higher probability of a better change than you would have otherwise. This assumes that not everyone on the team is a newb or a code-dabbling PHB. If they are, there's not much point in writing any code.
- alter8 14y agoA nice blog series on if-less programming (in Portuguese): http://alquerubim.blogspot.com/search/label/ifless http://alquerubim.blogspot.com/search/label/ifless
- lucian1900 14y agoReplacing static decisions with polymorphism is indeed often a good idea, but there's nothing wrong with using if when it's appropriate.
- killahpriest 14y agoThis is a terrible example of IF-less programming. The conditional is still here, it is just implied. IF: def method (object) if object.property param = object.property else param = default_param end end Claimed to be IF-less: def method (object) param = object.property || default_param end It may be easier to read, but in the end you are still writing an IF statement.
- dsego 14y agoIt's not only easier to read, but it clearly shows the intent, that it is only an assignment. It's faster to comprehend too, you don't even have to look at the right hand side if you're not interested. The first example obfuscates the intent a bit. Also, in other languages, you'd have to declare the variable first and that's even more lines of code.
- dragonwriter 14y agoIt would be just as clear in expressing that the intent is an assignment (and more clear what is being assigned, since it avoids the coalescing-OR) though less concise to leverage the fact that Ruby's "if" is an expression not a statement: <code> param = if object.property then object.property else default_param end </code> Though, actually if you want to do what the description says (use the default if the property is unassigned, represented conventionally in Ruby by the accessor returning nil) rather than what either the good or bad code does, you probably want: <code> param = if not object.property.nil? then object.property else default_param end </code> (The difference between this and the other versions shows up if the property is assigned, and its value is false.)
- killahpriest 14y agoThe code tag does nothing, do this instead: Text after a blank line that is indented by two or more spaces is reproduced verbatim. (This is intended for code.) http://news.ycombinator.com/formatdoc http://news.ycombinator.com/formatdoc
- praptak 14y agoThose who use if are obviously too flimsy to decide what their programs should do. I would not trust such persons to write any code at all.
- lclarkmichalek 14y agoThe first example is an actual example of how removing ifs can reduce complexity, but the last few seem misguided. It is no easier to test `collection.each {|item| result << item if item.condition? }` than `collections.select(&:condition)`; they are all but equivalent. The exception handling example doesn't actually show the benefit of not using ifs, it shows the benefits of using exceptions over return values. Setting up default values via || is also a nice trick, but it hardly makes a macro difference. Also, "# I slept during functional classes"? I don't know ruby, but the `each` method seems to be just a variant of map, which is a pretty fundamental functional construct.
- ajanuary 14y agoeach is basically an entirely non-functional variant of map if functional is defined as no side affects.
- raverbashing 14y agoThis is getting ridiculous " ifs are a smelly thing in a Object Oriented language." What's smelly is such a absurd assumption. Yes, let's replace every small decision in our program with inheritance and another level of abstraction, that will work out swell Please go read an Assembly manual because you have obviously forgotten what a computer is and does And then we end up with unmaintainable systems with a class hierarchy higher than the Empire State. Of course, as mentioned, replacing an if with a "software structure" (like pattern matching in an functional language) is beneficial, but to call it a code smell is ridiculous
- salboaie 14y agoRight, object oriented is not everything, sometimes ifs or switches are just fine. I would suggest that decision tables, rule engines, workflow, DSLs, even finite automaton and other abstractions etc are better than having many many objects that are avoiding ifs :)
- alisnic 14y ago> "ifs are a smelly thing in a Object Oriented language." It is partially my fault because I failed to express what I really ment. English not my native, so sorry. What I really ment in this sentece is "why using (a lot of) ifs can be a bad practice in a Object Oriented language"
- raverbashing 14y agoOk, with that I can agree. Or maybe better: how can ifs be replaced (sometimes) using OO principles
- jeremiep 14y agoMy take on why ifs are "bad" is that they are statements, just like for, while, switch and others. Being statements they can't be composed as you would do with expressions.
- dragonwriter 14y ago
- wildranter 14y agoDon't. Just don't. Do you think Da Vinci would've painted just with oil because people think real painters work just with that? No he didn't, and so you shouldn't too. Don't limit yourself just to blindly comply to some silly idea. Use everything you know to get the job done, and once you get it working, make it beautiful. If statements are an incredible tool. Just ask any Erlanger and they will either tell how much they miss it, or just lie to your face. ;)
- revscat 14y agoIf statements are rife for abuse and can be an indicator of poorly thought out structure. This article mimics my own experiences, namely that overuse of if statements is a smell and can usually be avoided to the benefit of the code. I long ago abandoned else clauses. It was a short time thereafter that I realized that if statements themselves weren't all that necessary, most of the time.
- colomon 14y agoThere is no programming construct that exists that is not rife for abuse.
- rejschaap 14y agoYeah, I've seen people go down this path. In the next step they realize they need an if-statement to determine whether to instantiate a PDFPrinter or a MSWordPrinter. Which they conveniently hide inside an AbstractPrinterFactory or a ComplexPrinterBuilder. This is probably how the whole Enterprise Java thing got started.
- arethuza 14y agoMy own pet theory is that 95% of the enterprise code carefully architected to be extensible is never extended.
- gutnor 14y agoSimilarly, when there are no extension points, extension get hacked in (= "if" everywhere !) Here is my experience with architected extension points: 1. Generally the first extension point used set the trend, other developers will follow the "pattern" blindly. Hacking to make it fit rather than use another more appropriate extension. That is both bad and common (everyone has had to work with too little time) 2. There is often a sharp refocus close before or after release 1.0. A lot of the extensions disappear at that stage (demo, experimental feature, cross-platform/framework support, performance targets are set, security infrastructure is decided, server setup, integration test env. available instead of simulated, ...). Structural change (like removing extension point) become very difficult to justify after release 1.0. 3. Technical debt is very often called "selling feature" at management level. But yeah, real world code sucks.
- riffraff 14y agoNot endorsing, but much more expanded by the Anti-IF campaign http://www.antiifcampaign.com/ http://www.antiifcampaign.com/ (which focuses on "bad IFs")
- barrkel 14y agoFunctional pattern matching (more rigorous IFs) is open for functions but closed for extension. You can write as many functions as you like, but you need to modify all of them if you add a new data type. OO polymorphism is closed for functions but open for extension. You have a fixed number of functions (methods), but you don't need to update all the call sites if you add a new data type - merely implement all the methods. The requirement for what needs to be open and what can be left closed is what determines which choice is better. For example, a GUI framework is best left open for extension, because every application GUI usually ends up with specific widgets custom-designed for that app - sticking with standard widgets tends to make apps form-heavy and lacking in polish. But for the widgets to work in the framework, they need a fixed set of methods to be consistently manipulable. A compiler AST is best left open for functions, because the majority of work on an AST is in algorithms that transform the AST. The language changes at a much slower pace than additional optimizations and analyses, and frequently new language additions can be represented in terms of existing AST constructs (where they are syntax sugar, effectively built-in macros). So having a more fixed set of AST node types is less of an issue. Choosing one or the other on the basis of the orthodox religion of OO or functional programming, meanwhile, is just obtuse.
- alisnic 14y agoI totally agree with you, the purpose of the article was to explore but not to obsess over the subject.
- kator 14y agoWelcome to HN where all subjects are obsessed over obsessively.. That said I do think all things in moderation. At some point if you abstract the logic so far out humans have a hard time reading it or computers have a hard time optimally running it then you've lost the gains. I can think of several times when I was coding something using a class and noticed I had to write a lot of logic to really use the class the way I wanted. I wish in those situations the class was better designed to avoid spaghetti code to use it. That said many times the use of an object is nothing like what the original author thought it would be. It is hard to think in advance about every potential use case, shoot for the most common cases and make it all readable. Hopefully later someone else will come along and make it more useful to the real world use cases.. :-)
- kayoone 14y agoYou know whats a smelly thing in OOP ? Overblown abstractions, design patterns and stuff for the sake of flexibility which in the end mostly will never need that kind of flexibility but in turn readability and clearness suffers instantly. I am all for Design Patterns and abstraction, but only when it makes sense.
- meaty 14y agoIn full agreement. We have a rule about only using patterns if they become apparent rather than selecting a pattern up front.
- viscanti 14y agoI guess I don't see the harm in spending a little extra time up front to design things properly rather than just jumping in and writing code, hoping to stumble upon the correct design pattern. I've found it much easier to find and solve problems up front rather than waiting for potential landmines, at least, the time required to fix those problems is less if they're exposed earlier in the process. It's certainly possible to over-engineer a solution if you select a design pattern up front, but maybe you'd be better suited by having a rule against over-engineering.
- lmm 14y agoIt's hard to define a rule against over-engineering. The only approach I've seen that has any success is to ban any and all design up front, and only ever write code to implement user-facing features.
- meaty 14y agoWe "spike" stuff i.e. do a quick sanity check before we commit to something. A pattern will become apparent there and then if there is one. Sometimes this spike is re-factored into the product or is scrapped and rethought. Multiple eyes see every design - that is more important than anything. You can't quantify over-engineering until it is done unfortunately.
- 14y ago
- jwilliams 14y agoThis is an aside, but don't use the ruby "to_proc" approach that listed in the article. i.e: result = collection.select(&:condition?) The "&:proc" methods are (very, very likely) slower and they also "leak". When I say "leak", the VM doesn't Garbage Collect the parameters of the proc until it is used again. Most of the time this is fine, but when it's not, you're wasting considerable amounts of memory. This is known and is considered within the spec. I know they are semantically equivalent, but the MRI is doing something weird internally. (ps. Learnt this the hard way).
- polskibus 14y agoIf you're doing "ifs" on the same condition sets in various functions then you should consider encapsulating the condition in class hierarchy. If there is just one if for a condition set, introducing a class hierarchy is just bloat.
- gbog 14y agoAn often better alternative to inheritance for conditionals is configuration with functions as values.
- sublimit 14y agoOh great. What will you people come up with next? Variableless programming?
- gordonguthrie 14y agoErlang is mostly IF-less (destructuring pattern matching in function heads) and doesn't have variables. A = 1, A = 2, % fatal error because 1 != 2 So, yeah, variabless programming FTW!
- dragonwriter 14y agoErlang has mutable variables (the process dictionary), it just makes you do more work to get at them instead of immutable ones and prevents them from being directly shared and causing synchronization problems.
- gordonguthrie 14y agoNo, the process dictionary is not a mutable variable - there is no natural idiom to use values stored in the process dictionary as variables in code, you have to get them out and put them in via immutable variables. Any given Erlang process has meta-information about itself, how many reductions it has, how big its heap is, which flags are set. These are the global state of the process. The process dictionary allows you to store and manipulate your own global state of the process - and the people (hands up, that includes me) get smart and use it as local state of the programme and then get their bum bitten badly and swear never to dance with the dark side again... :( Not bitter :)
- sublimit 14y agoThe way I see it, a programming language should... 1) correspond to computer architecture (which excuses the distance from human thinking) 2) correspond to human thinking (which excuses the distance from computer architecture) So what's the excuse for pulling such strange rules out of nowhere? The sort that have no counterpart outside of the language itself? Is it just for the sake of making programming more of a puzzle, or...?
- mwilliamson 14y agoAs an example of taking this to an extreme, I was at a coderetreat where we had to implement Conway's Game of Life without any if statements (or similar, such as switches) -- we had to use polymorphism instead. The result was that my partner and I ended up reimplementing a subset of the natural numbers as distinct classes. http://mike.zwobble.org/2012/12/polymorphism-and-reimplementing-integers/ http://mike.zwobble.org/2012/12/polymorphism-and-reimplement... I'm definitely not advocating this as good programming practice, but the point is that if you're used to always using if statements, then it's hard to learn alternatives. By forcing yourself to use the unfamiliar, you might find some situations where polymorphism is better suited to the problem, whereas you would have previously defaulted to using ifs. (barrkel has already left an excellent comment on when the two styles are useful, so I won't repeat it: http://news.ycombinator.com/item?id=4977487 http://news.ycombinator.com/item?id=4977487)
- bane 14y agoI'm a little surprised nobody is lamenting the performance hit this kind of technique will incur vs just using an if statement. (reaching into my way back machine, ifs essentially compile down to a few comparison instructions (which are often just subtractions) and a jmp instruction (depending on the platform), it's literally built into the processor! For a simple if statement we might be talking a handful of cycles to eval the if vs an extended call stack pumping and dumping exercise)
- neumann_alfred 14y agoFor inner loops etc., having less branch prediction misses or none at all can actually outweigh having to do slightly more complex calculations. http://stackoverflow.com/a/11227902 http://stackoverflow.com/a/11227902
- bane 14y agoI'm actually curious about the internals of how a modern OOP system works internally once it's boiled down to the CPU level. I'd imagine there's still lots of branch prediction issues in complex OOP systems.
- neumann_alfred 14y agoWell, that you don't use OOP for those inner loops is kinda taken for granted I think. That is, you certainly don't program for code beauty first and foremost -- OOP may not hurt in a particular case, but if it does, code beauty may have to go... example: http://www.particleincell.com/2012/memory-code-optimization/ http://www.particleincell.com/2012/memory-code-optimization/ (which is not about OOP per se, and not about branching, but illustrating that knowing what the CPU actually does (not just what it did decades ago) is really important when talking about performance)
- PaulHoule 14y agoThe problem isn't if, it's "else if". If-then-else ladders tend to evolve to be very difficult to understand, maintain and debug
- jayvanguard 14y ago2001 called, it wants its debate back. For all the new kids on the internet: OO doesn't enable re-use, inheritance generally sucks, and bloating your code with new types just to solve something three lines of if/then/else could solve isn't worth it.
- primitur 14y agoOne reason why "ifs are smelly" has become a maxim in some circles is because they represent an under-tested code path. In such areas as safety-critical/life systems, where a different codepath can be taken on the basis of a single var, this can be a very, very dangerous practice. Certainly in safety-critical, a reduction of "if"-based codepaths represents higher quality software in the end. I have seen cases of radiation-derived bit-rot which don't manifest in any way until a certain "if"-path is evaluated by the computer - this seriously does happen and can still happen in modern computers today. Having an abundance of such code switch points in a particularly large codebase can be a degree of complexity that nobody really wants to manage - or in the case of disaster, be responsible for .. so this maxim has been pretty solidly presented in industrial computing for a while. Make the decision-making as minimal as possible to get the job done, and don't over-rely on the ability of the computer to evaluate the expression in order to build robust software. Now, its sort of amusing that this has propagated into the higher-order realms of general application development by which most Class/Object-oriented developers are employed .. but it is still an equally valid position to take. State changes in an application can be implemented in a number of different ways, "if" being one of the more banal mechanisms - there are of course other mechanisms as well (duffs devices, etc.) which are equally testable, yet more robust - simply because they break sooner, and can thus be tested better. I take the position, however, that a well-designed class hierarchy won't need much navel-gazing decision-making, which is what the ol' "if (something == SOMETYPE)" statement really is: a kind of internal house-keeping mechanism being done by the computer at runtime, instead of at compile-time. So there is a balance to this maxim, and the key to it is this: how complex does it need to be, versus how complex can the codebase be before it becomes unmanageable. If you're not doing full code-coverage testing with 100% testing of potential codepaths, then every single if statement represents a potential bug you didn't catch yet.