Object Oriented Programming is Inherently Harmful
harmful.cat-v.org
harmful.cat-v.org
- The syntax. I like to be able to say thing.doSomething(). It doesn't always make sense, but sometimes subject-verb syntax is more natural than a function call.
- Polymorphic dispatch (is that the term?) to replace if blocks. Instead of `if (thing is Car) thing.drive() else if (thing is Boat) thing.swim()` its just thing.move(). Pattern matching in functional languages solves this in a different way.
- Interfaces are nice.
I guess I could be happy in a language that just has structs and functions, and some help in form of pattern matching, multimethods and so on.
Erlang
myfun({duck, "My Duck's Name"}) ->
dosomething;
myfun({dog, "My Dog's Name"}) ->
dosomething;
myfun({cat, "My Cat's Name"}) ->
dosomething.
Erlang also has arity matching (this function is different than the one above it): myfun({cat, "My Cat's Name"}, cage) ->
dosomething.
Case statements are one way of doing that but those are considered a messy style in Erlang because they end up nesting so deeply - keeping it all out in small little functions and using pattern and arity matching is the "right" way.You can do the same (except for arity matching) in Haskell with function arg pattern matching and Haskell also has some very nice projection tools for it too:
Haskell
newtype Name = Name { unwrapName :: String }
data Animal = Cat | Dog | Duck deriving (Eq, Show, Ord)
myfun :: Maybe Animal -> Name -> String
myfun (Just a) (unwrapName -> name) = (show a) ++ "'s name is: " ++ name
myfun Nothing _ = "No animal given!"
> let n = Name "Fido"
> myfun (Maybe Cat) n
> "Cat's name is: Fido"
>
> myfun Nothing n
> "No animal given!"
There are many problems with that function and I could probably eliminate the multiple function clauses and reduce it to one clause by getting rid of the pattern matching and using the maybe function: myfun :: Maybe Animal -> Name -> String
myfun a (unwrapName -> n) = maybe default formatAnimal a
where
default = "No animal given!"
formatAnimal t = printf "%s's name is: %s" (show t) n
That would be the more idiomatic way I would do it - pattern matching isn't "bad" in Haskell and you'll see it used quite often in utilities and libraries. It's considered good form to move stuff like that into a utility and give it a clear name (like the maybe function above that takes our formatAnimal function) then use that in your application code so it becomes obvious what's going on.Pattern matching, deeply nested ifs and cases, etc... are somewhat hard to parse for the eye.
newtype Name = Name { unwrapName :: String }
(unwrapName -> name)
I'm not familiar with this, are you pattern matching on a function type? type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn "Rectangle with length %f" h
| Circle(r) -> printfn "Circle with radius %f" r
Admittedly, this is not too far from a simple `if`, but I believe the language ensures that you handle all cases.The difference to OOP is that the code is in one place, with the function, instead of distributed, with all objects. It makes it easier to add functions, but harder to add new types. Often, this is the better option, but if you are writing truely extensible code (a library that gets passed custom objects, or an application with add-ins) then you want to go the OOP way.
The D language has a way of providing your first point without resorting to objects. Basically, the compiler just transforms a statement like `str.length()` into `length(str)`.
The other two seem harder to do in a simple way, but I'm sure someone has come up with something.
Consider that C# let's you have all three of your essentials:
It doesn't have higher-kinded types, but it does have monad comprehensions (LINQ + SelectMany)… the special syntax is nice but not necessary.
It has type classes in the form of implicit conversions.
It has modules in the form of static classes.
C# even lets you do Smalltalk-style OO without too much fuss.
The biggest problem with C# and pethaps OO in general is the lack of intellectual curiosity.
Could you expand on this?
That is, because your data and function are bundled together, having the data means you also know what function to call. Decoupling them, as in a functional paradigm, and passing the data into a pattern match, you have to perform some logic upon the data to figure out what function to apply to it.
[1] I think that the JITs for the CLR and JVM can do better than vtables in the cases of sealed/final classes.
Foo of int * bool * string
I don't have a full PC handy, but, while learning F# a few years ago, I remember using Reflector to analyze an assembly generated by FSC.exe and the pattern match turned into a class hierarchy.It may not work for all kinds of pattern matching, but that seems like the most straightforward way of handling sum types. What am I missing?
OO languages have trouble optimizing constructs such as interfaces (and other forms of multiple inheritance) because the v-table is no longer very linear. Now throw in arbitrary predicates and you are pretty much back to linear search. Probably the state of the art can be found in GHC, but even they probably fall back to linear in a lot of cases.
[1]: JIT. Just in case some people confuse concepts, I'm not talking about JVM, virtual machines and such. Just plain runtime machine language generation.
NOTE: Following is meant for dynamically dispatched lightweight methods, like those that just read (or compare) or store (or modify) some simple field value or do other simple operations where dispatch and call frame setup costs dominate. In inner loops which are executed enough many times that the codegen overhead is less.
The rationale is that branches are very expensive on modern CPUs, especially indirect subroutine (= method or function) calls. Consistently 14-20 clock cycles lost in addition to whatever time call frame setup takes, pushing and popping registers from stack. If inner loop cost is otherwise just a cycle or two and the dispatched method is trivial, it's easy to see how branch mispredict and call setup costs can dominate. In some cases some unrolling and prefetching could be done in addition.
In this case I envision routines that fuse (inline) dynamically dispatched (function pointer, message passing, pattern recognition) method inside a loop. A compiler could generate the rules and runtime system can do the actual composition. The method would then be dynamically inlined within the loop. No call, no function pointer call (=vtable), no full pattern recognition, no message dispatch, etc. Just fused, inlined code.
Pattern match can't be completely resolved early? Just generate code inside the loop that does the rest of the work with tests and branches. Try the most likely case always first (may require some profiling). Same with other dynamic dispatch.
Many cases, but known exponential distribution of probabilities? Inline some most likely cases, dynamically dispatch the rest. Like before, most likely first and some profiling may be required.
This way very expensive (data dependent) indirect function call is avoided. Call frame setup and teardown is avoided. The savings can be an order of magnitude for short dispatched methods.
Definitely no complete virtual machine needed.
Note: Edited multiple times.
If in the inner loop, the same method is called (of the same class), this means that the target of the indirect function call is always the same, and I assume that the CPU can predict that. If not, the compiler can hoist the lookup out of the loop. In certain languages, e.g. in Objective C (where lookups are dynamic, and much more expensive than vtable approach), it's a common programming pattern to manually hoist the lookup out of the loop, get the method descriptor (i.e. the jump target) and call the descriptor in the loop, without going through the lookup again.
If the inner loop loops over objects of different class (i.e. you're looping over an array of objects, each can be a different sub-class, and you're calling a virtual method), I don't think there is any way to improve vtable based approach.
That is for correctly branch predicted calls.
Far worse for mispredicted calls.
> If the inner loop loops over objects of different class (i.e. you're looping over an array of objects, each can be a different sub-class, and you're calling a virtual method), I don't think there is any way to improve vtable based approach.
Say 90% of the objects are of certain same type and the code generator knows that. Codegen puts the test for this case as first one and inlines the code directly in the loop body. Then you can at least have those 5-10x savings in 90% of cases and have branch mispredicts for the rest of the time. That can still be a significant saving.
In any case, dynamic dispatch, polymorphic or not, is considerably slower than static polymorphic dispatch, which Rust excels at. Servo's biggest performance wins over Gecko (outside of pervasive parallelism) have come by greatly reducing dynamic dispatch.
I expect the issue to be revisited post-1.0. In the meantime, if you'd like to familiarize yourself with some of the proposals, here's a recent-ish summary and discussion: http://discuss.rust-lang.org/t/summary-of-efficient-inherita...
My other goal was to show that Rust gives you enough tools to manually emulate an efficient inheritance-style dynamic dispatch scheme, even if it can't fully prove that your implementation is safe.
So pretty much Rust you mean?
As in any situation, to understand what someone is saying, you have to know who they are talking to.
People who hate OO always refer to overly bloated systems that treat OO as a philosophical foundation, something to be religiously adhered to. I'm sure those systems exist and I'm sure that they're horrible. But, y'know, it's just awfully nice to have an easy way to define structures and functions that operate on them, and the ability to use some structures interchangeably with other, related ones.
Objects or Haskell-like records, I'm happy with either. I like multiple inheritance but mixins are pretty neat too. Encapsulation is a good thing, but I could probably learn to live with any kind of decent modularity and separation of concerns. Et cetera.
> Pattern matching in functional languages solves this [polymorphic dispatch] in a different way.
Pattern-matching doesn't give you late binding / ad-hoc polymorphism though, so while it's useful to replace static if statements or case statements, it's not a full replacement for OO's polymorphic dispatch. For that you need things like typeclasses in Haskell or multimethods in lisps.
Wadler's famous expression problem is quite relevant here: http://homepages.inf.ed.ac.uk/wadler/papers/expression/expre...
It looks like you're talking about Predicate Dispatch. http://c2.com/cgi-bin/wiki?PredicateDispatching
You'll encounter the term in Bertrand Meyer's Object-Oriented Software Construction for example: http://www.goodreads.com/book/show/946106.Object_Oriented_So...
>Pattern matching in functional languages solves this in a different way.
captainmuon wanted a more flexible way of dispatching on types similar to what can be done with Pattern matching (which is also a special case of predicate dispatch).
In the post I linked to above this example is given:
Basically, instead of basing the dispatch on an "is_a" check, you check whether a general predicate is valid on the argument. So, in imaginary syntax instead of writing
int foo( int a, int b):
if a > 0
return a
else
return b-a
you'd write: int foo ( gt_zero? a, int b): return a
int foo ( int a, int b): return bMy pet hate is how dogmatic some people get to the point where they get angry over the use of if/switch statements. "It's not good OOP, use polymorphism", well the goal isn't to pass an OOP exam, but rather to write clean maintainable code where possible. If an if/switch saves me writing 6 classes with 3 lines in each of them, then that's what I'll do.
Consider a simple information system consisting of a database and a web service publishing a REST/JSON API. The web service will need a definition of the interface between itself and the database (the DAO) and a definition of the interface between itself and the outside world (the DTO).
Defining both interfaces explicitly allows you to control change and accessibility of data. I.e. you don't have to expose your database to consumers of the web service.
I'd love it if my OO language of choice (C#) could add a special non-polymorphic type to represent these concepts, because then you could do cool stuff like define the DTO in terms of the DAO. "The DTO is all properties of the DAO except propertyX and propertyY".
I always figured OOP programmers actually hate coding because they try so hard to avoid actually writing code that does work.
Sounds like you have a team of Farchitects.
I have the title "architect", and I write code. It's usually code that lets the other thirty programmers write one or two lines of code instead of 200 lines, 30 times, in 17 different ways.
Because it is framework code, I'm often providing them the Lego blocks to build the rest of their system in... With unit tests... and examples... and at least one real implementation... and a Wiki page explaining it... and a Powerpoint session teaching them how I expect them to use it.
I do get the occasional grumble ("Uhh... Can't I just use Doohickey V directly?" "Sure, take a look at the interface, and don't forget the externalized configuration." "Oh.")
Just sayin'. There are architects and there are Architects.
It is something that is part of enterprise culture, it has nothing to do with programming languages.
"FO is the “structured programming” snake oil of the 10s. Useful at times, but hardly the “end all” programing paradigm some like to make out of it.
And, at least in it’s most popular forms, it’s can be extremely harmful and dramatically increase complexity."
It's much easier to be a web contrarian than to build widely-used solutions that don't have limitations, drawbacks, inconsistencies, or other issues.
Well, turns out CLOS is so good it's hard to stay away from it. You don't struggle to force something into an inappropriate object-oriented paradigm, rather, object-oriented solutions just flow naturally from the problem. And it's a pleasure to use.
I often find myself suspecting that the hate OO gets is primarily a result of this early error.
The remainder is of course the way OO was promised to be the end-all of programming paradigms, which has not happened. Backlash is fully justified for that.
To make matters more difficult, you have to make these kind of decisions very early before you write your code. By the time you properly understand the problem, you will have invested in a particular solution. There is N>3 ways to do things and there is only one best way, so chances are you are doing it the wrong way. OOP is not bad, it's just often misused.
There is a huge gap between what OO could/was meant to be and how it is seen nowadays. I prefer thinking of OO as it's implemented in Smalltalk or Io instead of in C++ or Java.
To be more specific, in Smalltalk your hello world would look like this:
Transcript show: 'Hello world'
and in Io: "Hello world" println
No class/object declaration in sight, right?And then this:
> The root problem seems to be trying to do eveything the OO way when all you need is a function
is a wrong question altogether - there is nothing stopping you from writing a function in OO (other than broken and dumbed down implementations in major languages, that is). In Smalltalk:
my_func := [ 'Look, I'm a function!' ].
"There's this slightly unusual way of calling the function though:"
my_func value. "returns 'Look, I'm a function!'"
similarly in Io: my_func := block( "Look, I'm a function!" )
my_func call # same as above
So, to make my point clear: there is NOTHING in OOP itself which REQUIRES verbosity and over-abstraction. On the contrary: going "full OO" makes it easier to write short, readable, to-the-point code. It also makes it easy to use FP patterns should you want it.What you're arguing against are the currently popular implementations of OO, which are just bad. And before I forget: Erlang (and I program in it quite a bit) is one of the best Object Oriented languages I worked with.
> As Erlang became popular we were often asked “Is Erlang OO” - well, of course the true answer was “No of course not” - but we didn’t to say this out loud - so we invented a serious of ingenious ways of answering the question that were designed to give the impression that Erlang was (sort of) OO (If you waved your hands a lot) but not really (If you listened to what we actually said, and read the small print carefully).
http://harmful.cat-v.org/software/OO_programming/why_oo_suck...
On the other hand, if you go back to what Alan Kay had in mind when he invented OO, namely objects as "black boxes, similar to computers in miniature" and "message passing as the only way of doing something" then Erlang is very, very much OO. Its processes are objects, and sending (asynchronous!) messages is built into the language. Then you get encapsulation, data hiding and interfaces with module exports and behaviours. That doesn't stop Erlang from being FP, too.
Really, there are many different paradigms and many implementations of each one, it benefits no one to only consider one particular implementation as representative for a whole paradigm.
Footnote: Alan Kay post on a similar topic: http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-...
void my_func(){ std::cout << "I'm a function"; }
produces a simple pointer.Of course, this is how it should be - it follows from C++ design goals and totally makes sense for a number of reasons; however, from the perspective of OOP, this makes C++ less "pure OO" than mentioned languages (and many others).
I think the bigger problem has been decades of education that start with decomposing domain objects into OOP class hierarchies by common attributes.
Animal, Dog, English Bulldog, Herman.
Now we have significantly better ways of teaching OOP:
- favor composition over inheritance
- decompose and group behaviors
- Liskov substitution principle
Sadly we're dealing with mainstream languages that are barely OO like Java (which I've done 99% of my work in for the last thirteen years). But even if Eiffel had somehow taken off and stood in Java's place... Nope... can't finish that sentence. It couldn't have.
OO was originally created as a way to cleanly allocate memory on the heap. That's a fairly well solved problem now. The best parts of OO aside from heap allocation are encapsulation and polymorphic dispatch (two sides of the same coin). Closures and functions as fundamental units of decomposition handle that fairly well, especially when you couple that with type inferencing.
I'm not arguing that OO is bad. I'm arguing that it's no longer anywhere near as useful as it used to be, even when it was implemented and used properly.
That's the first time I hear something like this. Any source for this?
Given that the summaries you read on Wikipedia about it describe Simula 67 as a fulfillment of the need to create a better process description abstraction, I may simply be wrong.
my_func := [ 'Look, I'm a function!' ].
How does the string parsing work here? I like it a lotedit: welp, this [1] makes it sound like 'Look, I'm a function!' is a typo and should in fact be 'Look, I''m a function!' (Note the double simple quote `I''m`)
But the idea to not need to escape single quotes when surrounded by something else than whitespace is interesting.
[1] https://gist.github.com/sin3141592/602700#file-smalltalk-gra...
As for functional vs oop, I am on the opinion that just because the haskell type system is awesome, and the java typesystem is traditionally unflexible, it doesn't mean functional > OOP. Language features like objects, static checking, first class functions are typically very orthogonal, hence you should be combining the features, so they match your domain.
I figure as code evolves, it generally takes 3-4 rewrites to get class hierarchies to a point where it matches your domain and more often than not the solution involves shallow trees and the use of traits or mixins which is something traditional oop languages don't all support well.
The only harmful paradigm is "write only programming", everything else is negotiable.
The main value of functional programming is working with values and isolating mutation. When you deal with just immutable values your code maps to distributed systems naturally which is why functional programming is becoming more popular with the advent of cloud and distributed computing.
I work with hardcore distributed systems people (the ones that attend SOSP and OSDI). And the penetration of functional programming in distributed systems is about 0%. Sure, immutable state is easy to not share, but sharing of mutable state is inevitable, and you gotta deal with it, not try to wish it away.
The only people who seem to think functional programming is great for distributed systems seems to be people who don't really do distributed systems, or at least ones where scalability, performance, and fault tolerance are critical.
No, it certainly does not mean that. And there is a tendency to misattribute the benefits of FP to the type system and vice versa. So remove the type system from the equation. The fact that erlang is better than all the generic untyped OO languages suggests that yes, FP is better.
>I figure as code evolves, it generally takes 3-4 rewrites to get class hierarchies to a point where it matches your domain
That's not a good sales pitch for OOP.
What criteria are you using to determine this "fact"?
That said, I prefer a hybrid approach which is why I use Scala. It's not an either/or (or, if you prefer, an Option monad :-)
Just because you can build a house with a rock doesn't make it the best tool for the job, and it's in the interest of the carpenters to figure out what is the best tool for the job.
Combine that with object mutable state. And that with multi-threading (surprise state changes). Add exceptions on top (surprise hidden gotos, especially annoying in C++). The end result is often nearly impossible to fully understand.
However, this is not anti-OOP rant really. I recognize OOP has its uses.
The bigger issue is almost always when there's a discussion whether X is better than Y, some people seem to forget often both X and Y have their place in the toolbox. They're often complementary. When you like technology X, technology Y is not a threat to you, but an opportunity to learn something new. In the same way, criticism against X can be an opportunity to learn and improve. Not hostility against people who like or are used to technology X. No matter how much you like hammer doesn't make a saw a bad tool.
Right click -> Find all references / go to definition don't do that faster than typing out the grep command?
You said mutable state makes it harder to understand, and you said exceptions make it harder to understand because they're hidden gotos (which if statements and while loops are too), but didn't explain either. In my experience, exceptions have been a very simple, helpful, intuitive component of control flow.
Yes, IDE is not generally faster. Find all references is not faster for me in most real life scenarios. Typing grep command is just arrow up, ctrl-something to get the cursor to right position, type something, hit enter. Piped to less, I hit / to search within the results. Done right, results from grep appear pretty much instantly, no waiting. Some operating systems are much better for this technique than others, due to cost of dealing with small files.
> You said mutable state makes it harder to understand, and you said exceptions make it harder to understand because they're hidden gotos (which if statements and while loops are too), but didn't explain either. In my experience, exceptions have been a very simple, helpful, intuitive component of control flow.
Whenever you call some method (or overloaded operator), it can throw. The fact is completely hidden from the context in front of you in C++.
In Java, you do know what might throw, because you have to declare it. On the other hand, the cost is high when anything you call changes the exceptions it throws. Or more like, it's set in stone.
It is better when it's specified exactly what exceptions can be thrown by a method, yes. One of the only things I prefer in Java over C#.
Git grep is great too.
Regardless, if it works for you, great. We should all use what works and disregard needless debate and politics.
In the end, typical OO leads to type systems which lead to noun discrimination which are ontologies, which reflect world-views. You don't want to get any of that on you. The more successful type systems stop at grouping aggregatable functions. Look at Go. Or Clojure. Those type systems are as good as it gets to me but I've never written a line of Smalltalk or Haskel, so someone set me straight on that.
well FP purists would argue that objects(a unit that has properties and methods, and can pass messages to collaborators)have states.AFAIK "pure FP" is about getting rid of states,using immutable datastructures, and describing operations on stateless units,rather than writing imperative code.
I personally believe that being a FP purist or OOP purist doesnt make sense.Both paradigms can work together thus be orthogonal and not in opposition.
Of course not. It's about making state be always explicit and about carefully controlling side-effects. Some state is going to be there always.
The most simple program is easily done in assembly- or basic-style program - straight sequence of control instructions. The program starts with a set of data, constructs an intermediate data structure, a state graph, iterates on that graph in a simple sequence of commands, mutating it, collects output from the graph and then terminates.
When a program gets more complicated, we break sequence of commands into subsequences (subroutines/functions/etc). Each subroutine can be understood in isolation from others, which is a great win, but it still interacts directly with various pieces of the state graph, which limits our ability to reason about its effects.
Next we carve out individual pieces of the application state graph, and proclaim that each such piece can only be directly accessed by a small amount of code married to that piece of data. Such code and data married together are called an "object", and the entire approach is "object-oriented". Thus to reason about a subroutine we only need to look at its code in terms of its interaction with the "object" abstractions, rather than with the raw state graph data. Since abstractions are simpler than their implementations, it gives us extra ability to understand and thus create larger programs.
Implicit in the last two approaches is mutability - as the graph state changes, the same pieces of code continue to have access to it, and thus are expected to accomodate the drifting state. That is, the same chunks of code are expected to operate with a number of different states of the graph, corresponding to different stages in the lifecycle of the data.
An alternative way to cut down on complexity is "pure functional". In this approach each piece of code operates directly on the entire data graph, but only at a single stage of the lifecycle - it would iterate over an input readonly graph, then produce a new output readonly graph as a result, to be fed into the next stage of the pipeline.
So we have two dimensions - data and time. If we narrow down the data dimension, we end up with object-oriented approach. If we squish the time dimensions to a single point, we end up with pure-functional approach. The third dimensions - sequence of commands, is split up into smaller chunks in either of the two approaches.
I completely agree with the arguments showing how harmful OOP abuse is (especially branching inheritance trees!), just like abusing pointers, or structs, or error codes, or abusing <insert_language_feature_here> is harmful. But what does that have to do with OOP inherently?
Let's look at historical crashes, glitches, and hopelessly messy code and try to count their actual source. I bet you'll find sloppy programmers as the #1 root cause (maybe null-able pointers and/or weak typing as #2). OOP isn't bad because it's OOP, it's bad because hordes of bad programmers use it at big companies who don't care about code quality.
Beautiful functional code is beautiful.
Beautiful procedural code is beautiful.
Beautiful object oriented code is beautiful.
...
Spend more time writing beautiful code that draws its elegance from the rich set of paradigms and tools we have; spend less time participating in fad paradigm-ocide movements :)
> How would you do this without some kind of "object orientation"?
Variables. Variables are not "objects". What is an "object" anyway ? It's a shallow abstract concept which is impractical.
> The moment you formulate an "object" in code with interactions on that object
No that's not what the article is trying to talk about.
What people argue against OOP is how it's applied to anything for any case. Creating new classes/type/abstract concept each time you want to realize something can often lead to code bloat. How many classes named "XXX_manager" can one encounter ?
> Sorry for shattering everyone's hopes and dreams of coding pure OOPless.
OOP tends to have state, which doesn't bode well will multithreading, which tend to favor functional programming. I don't know if the future is going to be massively multithreaded, but I just think that OOP is just another way to structure a program, nothing more. Processes and threads already are objects. Functions too. So why tell students to create new types all the time ?
It seems to me that GUI and OO are deeply synergetic.
http://stackoverflow.com/questions/2672791/is-functional-gui...
http://blog.reactiveprogramming.org/
http://stackoverflow.com/questions/1028250/what-is-functiona...
- http://en.wikipedia.org/wiki/Sketchpad
- https://www.youtube.com/watch?v=USyoT_Ha_bA
- http://en.wikipedia.org/wiki/Simula
- The Development of the Simula Languages in HOPL I if you can get it, else http://phobos.ramapo.edu/~ldant/datascope/simula%20history.p...
What we know of UI today is more an artifact of smalltalk (both the first GUIs and smaltalk came out of parc and were related).
I have always imagined that it was just a natural progression from procedural code:
1. Put a bunch of functions in a file.
2. Ok, now we have too many functions to keep track of, so let's put functions in separate files.
3. That worked for a while, but now too much repeated code, let's put some functions into modules.
4. Now, we have all of these variables to keep track of, so let's hide some of them to lessen any confusion.
5. Etc. Etc. Etc.
6. Wow, now we have a complex language with all sorts of patterns that perhaps confuses things more than intended.
That's why I like languages that are lean and have pragmatic features. OO, functions, whatever works to make my life easier.
"OO is the “structured programming” snake oil of the 90' Useful at times, but hardly the “end all” programing paradigm some like to make out of it."
In 2014, I think it's important to remember that "structured programming" was, basically, "hey let's use FOR and WHILE loops, and subroutines, instead of a big pile of GOTOs", and think about what that says about any author who describes it as "useful at times" and "snake oil."
One of the big problems with discussions around OOP is that people have wildly different opinions on what OOP is. Whatever it turns out to be, it'll be a tool like any else that can be used efficiently or poorly.
Can we all just make a resolution not to use any technique (no matter how fashionable, not even FP) on a problem unless it is a natural fit?
Modelling something like a GUI toolkit is very difficult in Haskell, because each widget has a different clump of data associated with it. Functional programming works well when the data is uniform, or at least known ahead of time. It's very difficult to add new data structures into the system in a dependent module, like if you want to add a custom widget type. In OO, this is trivial.
I'm not sure what the best solution is. People are researching alternative solutions (namely FRP), but it's too bad it's necessary in the first place.
Add a custom widget from a gui program itself? So it's a gui program that has a widget builder then? Why not have an abstract data type which lets you encode the properties of both of those?
self.player.addComponent(Controller())
self.player.addComponent(Shooter())
self.player.addComponent(Jumper())
self.player.addComponent(Life())
self.player.addComponent(CharacterPhysics())
People who generally code applications could learn a lot from game developers and game engines. I took inspiration from Stencil and HaXe (but using Swift and SpriteKit)
http://www.stencyl.comA lot of the complaints I've read about OO center around people doing stuff that they later determine they didn't need to do, and then being stuck with it because it's in the middle of an inheritance hierarchy. Modern OO design would center around decomposing each class in the inheritance hierarchy such that the decomposition produces small subsets of shared behavior, and then composing those together to create the actual object. As a bonus you can still produce run-time polymorphism without crawling an inheritance hierarchy.
Composition in an OO system is very much like composition in mathematics. Two objects are combined to produce the desired behavior.
A lot of people crap on the GOF book because the patterns in it are either obvious or useless, but reading it does teach you some useful concepts, and for many people it will be their first direct exposure to something other than inheritance. Composition/Aggregation are great takeaways from that text.
As an example of a crappy inheritance hierarchy, a developer 20 years my senior had this four-layer inheritance hierarchy to represent 3 different data types. After he gave it to me I spent 15% of my time convincing him to let me eliminate half the classes he wrote. The worst part was that he was inheriting and then in the subclass he was writing functions that were semantically identical to superclass or cousin-class functions, but with different names. After I eliminated all that I was able to use templates to hide a lot of the mess.
OO design should be done according to the YAGNI principle--You Aren't Going To Need It--and its corrolary--do it as soon as you need it.
Yeah, that's the impression I get. The ability to add functionality to an object in small modules (Components/Behaviours)
However this only works if there is a inherent structure and ability to use components. Most frameworks and libraries I have worked with don't really have that structure.
For instance, SpriteKit (Apples 2d framework) lacks a structure for building components. So most SpriteKit examples tend to have deep hierarchies of class inheritance.
If you'd want to take a look I wrote this https://github.com/seivan/SpriteKitComposition
Documentation is still lacking as I am playing around with function names but it has a test suites that demonstrates usage.
I certainly remember many of the quotes when they first came about. I’m a product of the object-oriented wave. At the time I felt I had all the arguments as to why things were so much better with (or so much worse without) OO. In many cases, looking back I realized I was basically arguing for better tools and/or slightly better adherence to a few conventions.
My lessons from going through each “language” transition from debates over “assembler is the only way” through today’s DSLs and more:
1. OO wasn’t good or bad intrinsically. The principles, however, can easily lead to more manageable or coherent code over time. By and large, inheritance, polymorphism, encapsulation, abstraction all form the foundation of large scale systems.
2. Languages can do a great deal of harm, not concepts. Far too often, engineers dive into a new language or paradigm and assume all the code needs to exhibit all the properties of the new religion. In the early days of C++ programming, the saying we had on our projects was “a framework is not a compiler test suite”. I think in all languages, especially today, the risk is that you more harm than benefit when you try to do everything in some fancy new way. Maybe there is a role of operator overloading or templates in C++ but I never really found it. But I am pretty certain nearly every framework employed these techniques. You can’t blame the language because every language has stuff you can abuse. You can blame the zealots or evangelists which often cause the most challenges.
3. Language and paradigm innovation benefits rarely scale in very large systems, but small systems early on exhibit amazing benefits. Most engineers are seduced by new languages and paradigms (OO was just the one that came after structured programming and before functional and others). In the beginning, the new language or approach is amazing. Always amazing. Over time the real world shows up and every new engineer feels that the code is bloated and needs a rewrite when they join a project. Efficiency declines. The magic fades as reality dominates. With more than a few engineers the complexity of interconnection between parts of the code base trumps the simplicity and elegance within one part of the architecture. Expressing those in paradigm or language elegant ways approaches a very high degree of difficulty over time. At one extreme we see competitions of “hello world” or the most basic app all being amazingly simple. At the other extreme we see a constant breakdown in even the most basic methodological approaches. Even “Goto” was hard to do without and certainly in OO maintaining a pure inheritance model, public/private data, or more become as close to impossible.
4. In algorithmic complexity terms, a language or paradigm is at best a constant factor improvement over any other choice. The age-old rule of thumb is that programmer productivity is language independent. While I have no doubt that one could not spin up a new social network in assembler, one would be equally hard pressed to write a device driver or graphics runtime in Ruby or Python. Part of why methodologies gain attention is because the runtimes/libraries/frameworks that come with them do the things that need to be done the way that people want them to work today---that’s what gives the appearance of improved productivity. The right libraries in C can serve a great purpose. We see this when a language gets a new library that seems to bring renewed interest to it.
5. Tools are everything. What makes or breaks a paradigm/language are tools. You can take a simple language or paradigm and have great tools and become much more productive than a “better” paradigm with poor or ill-suited tools. One way that this surfaced over time was with tools that generated the right code—interface builders for example. Then using complex, archaic, or intensely manual approaches lacking formal foundations would become much easier. Plus the bonus of transparency of code generation really helped because other tools could easily integrate (having access to a whole tool system is also more productive than any one methodology+tool).
Ultimately, I think OO is perfectly good and most all modern systems make use of the 4 basic pillars of the paradigm. I don’t think it ever became the answer to code reuse or code quality that proponents claimed. Ultimately the methodology is going to be trumped by scale and age of code and system. Any success means your ability to start over is reduced and so the best bet is to focus on knowing what principles your project is being created and run with.
There is. For templates I'd think the role is obvious, for operator overloading, at least operator-> is indispensable.
It's useful in vector math, complex number math, matrix math.
This. 1000 times over. This is why well factored code bases can often seem to have lots of redundant classes and interfaces to inexperienced developers. It's all about maintainability over time, building bulkheads around change.
> tools that generated the right code—interface builders for example
The irony is that these tools often fail when the benefit is considered over time. This comes through a lack of tools - their code works badly with SCM (no domain-specific diff tools). They throw away everything we've learnt about software engineering just to win the marketing demo's.
I'll narrow down my question some more:
How does duplicated code improve maintainability over time?
I completely agree. In my experience, and it's probably the same for many people, I spend a non-trivial amount of time not writing "primary" code. Instead I'm testing, debugging, dealing with build issues and dependencies, packaging, wrangling deployments etc...
Operator overloading like using + on strings or adding n-space vectors to other n-space vectors is at least convenient, isn't it?
The day I understood that class and struct are merely the same thing, I questionned the purpose of private: and public:.
To be honest, I think the sole purpose of those construct is just to hide proprietary code when you sell libraries and deliver a set of binaries and headers, to force the compiler to forbid you from writing to, or using protected member. It's really moot. It's just a coding practice, it's not some grand way to think and construct applications.
OOP is nice if you want to create some non standard simple type, like Vec3, or when you want to use an interface for something quite complex. Templates are a good extension of OOP, but it's not used that much, even while the STL justifies the existence of templates. But other than that, when you want to make an end-user application which is not a library, I think OOP is not that much useful. Too few programmers write libraries, most just write applications, and applications don't need OOP.
If you use OOP to pretend that your code is reusable, please, first check if what you're making really is worth reusing, chances are it's not.
TL;DR: OOP is good to design bricks. Most coders are not brick designers, thus most programmers should not touch OOP that much.
If you make everything public and writable you have to spend a lot of thought on things that a compiler could help you with so you can focus on solving the problem. Much like using a type system.
Really depends on what he meant by secure.I'm french too.Encapsulation isnt about shipping proprietary binaries.You dont need OOP for that,furthermore,almost anything can be decompiled so your point is moot.
You'll understand when you work with a team,you'll understand the value of encapsulation, and forcing every developper to work with well defined interfaces that cannot be violated.Encapsulation makes communication easier,testing easer,it makes everything easier.
> Templates are a good extension of OOP, but it's not used that much, even while the STL justifies the existence of templates.
I would argue templates have little to do with OOP.It's a tool that allows genericity.You could have generics in a non OO language.
> It's just a coding practice, it's not some grand way to think and construct applications.
No it's a feature not a practice.The alternative is ugly stuff like in javascript where you put _ before prototype methods to express an intent. So much that ES6 comes with symbols because it shouldnt be just an intent.A developper should be able to express what he means,no just an intent(still possible in ES5,with priviledge methods but noT compatible with prototypal inheritance).
OOP is a tool,encapsulation is one of the most important things in OOP.Wether a language implement explicit encapsulation or not is of course an implementation detail.
Coders also tried revolving their procedures (casually called functions) around their objects - get_student_name(struct student), calculate_ranks(struct student_list) and so on.
It was necessary to have a paradigm that represented code very close to how we see this world. OOPs just had to happen.. it was not avoidable.
Now, the challenge lies in the fact that with everything powerful, one can go ahead and use it incorrectly. There comes the harm. But then there are better ways to learn the concept. I wrote a blog about OOPs recently. Read it here http://wp.me/p5jxzK-1b
Go ahead and use it, OOPs isnt going anywhere soon and it aint that harmful :)
http://kossip.ameyo.com/2014/11/25/the-unusual-approach-to-o...
For inheritance, I've never tried to understand it or use it. Yes, the .NET Framework apparently uses inheritance, but there as far as I can see mostly the inheritance is mostly just imaginary, mostly just a means of documentation. E.g., I can have a variable X of type byte, integer, float, GUID, etc. and write X.ToString, that is, pass (it really is essentially call-return semantics) the method ToString inherited from somewhere, some abstract class or some such, but I don't believe that there is actually any real inheriting going on. Maybe there is some overloading, but then the spelling 'ToString' is just a convenience for documentation and usage. Or, just as easily it could be that in case the type of X was integer, then I could write X.ToStringInt where the method ToStringInt was for converting integers to strings. Fine with me. I know; I know: If I can write X.ToString when the type of X is an integer, then where X is declared I can change its type to, say, GUID and keep the code X.ToString and, thus, get code rewuse, but I'd nearly never do that! Instead I'd want to check over my code to be sure the code still did what I wanted it to do.
Mostly I look at instances of classes much like in PL/I where there were data structures like
Declare
n_points Integer,
1 A Based,
2 Coordinates( n_points ),
2 X Float,
2 Y Float,
2 Lengths (n_points) Float;
So, in Visual Basic .NET could writeReuse in UI frameworks has been great, and I've had similar success with custom frameworks.
However, success invariably contains the seeds of failure, because success means that we are taken to the limits of applicability. To me, those limits were visible in the 90ies when I wrote my Master Thesis[1], issues like the non-composability of frameworks, the runtime/compile-time, composition/inheritance dichotomies, architectural mismatch etc.
Alas, nothing really happened, and IMHO, things actually got worse. At the time, I had two candidates for "the future", one being AOP and the other software architecture. AOP was a dud, but I am very hopeful about better linguistic support for software architecture, so much that I am creating a programming language that has software architecture as its organizing principle, deriving other paradigms such as OO from this base [2].
Having been exposed to FP early on, I have to admit I don't understand the current hype, because it seems to primarily address issues of "programming in the small" (tight coupling), although some of the lessons (communicate using simple data) are applicable elsewhere, and have been discovered elsewhere. I don't see a large-scale distributed system like the WWW built in the FP-paradigm, but happy to be corrected!
Taking the definitions from "Software Architecture: Perspectives on an Emerging Discipline" [1], you have the following:
- components
- connectors
- configurations (systems)
Guice (and other dependency injection frameworks) clearly address the third part: configurations. AOP is, at best, an implementation technique.
[1] http://www.amazon.com/Software-Architecture-Perspectives-Eme...
I'm a fan of hybrid OO/functional approaches, use classes but minimize side effects. Return new objects when relevant, etc.
It's a mistake to misjudge OO based on some over-bloated java-design or some underdesigned implication.
The phrase "everything in moderation" largely applies.
Given, if you are doing things in C, and passing a structure as a first argument, you are getting most of the way there.
I think inheritance is frequently over-applied, interfaces are a great idea (or even just duck typing), but not everything is an inheritance hierachy.
Rather, encapsulation is the most powerful concept.
Much of what gives OO a good or bad name is in the hands of who is doing the architecture - abstracting things too early leads people to occasionally go to the extreme other side of the fence.
There's a balance to be had, and advantage to learning from multiple schools of thought.
That has only gotten more true with DDR3 and DDR4 memory and giant caches. It is significant enough that it would behoove compiler optimizers to pause trying to do things with fewer instructions and start looking at better data organization.
“The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.” – Joe Armstrong
As with formalism you should know how OOP works and how seriously others take it, so you don't get into fights with your relatives at Thanskgiving.
That said, I do think OO is good for problem areas that actually have fairly static interfaces, like GUI libraries, for instance
This approach begins to suffer when application state is mixed between a remote server and a local client. Further, the physical metaphors of OOP suffer when removed from Smalltalk-like systems.
The rise of functional programming has as much to do with the rise of networked computing and shared data than anything else.
Programming is like kung-fu. You gotta pick and choose the best of each style, when they are relevant.
( http://harmful.cat-v.org/society/gay_marriage http://harmful.cat-v.org/political-correctness/girls-in-CS http://harmful.cat-v.org/economics/fair_trade )
Wow. Any health male makes sexist jokes to women if he gets to know them? I guess I'm just not very healthy then.
"My wife’s male coworkers behave the same way and I have no problem with that."[0]
Easy to say you don't have a problem with something that doesn't affect you!
0: http://harmful.cat-v.org/political-correctness/girls-in-CS
I want to write relatively simple code, thus, want to avoid tricky features of the software tools I use, and so far have been successful. But this desire means that I don't have deep experience with tricky aspects of OOP. So, YMMV.
So far mostly my code looks just like it would have before OOP; I've created only a few OOP classes; and all of those are simple. I write a lot of functions but not many classes.
Some things I do like:
(1) Can have an array of classes and, then, can sort the array with whatever class properties want to use as the sorting keys.
(2) Can serialize an instance of a class to a byte array, send the byte array via TCP/IP, and deserialize the result.
Well, the OP notes some of the dangers of inheritance. I agree and saw the danger right away and, thus, in the code I write try not to use inheritance and so far have been fully successful.
But, I can think of situations where inheritance could be useful and keep the work better organized than not using inheritance.
But, I'd say: The main issue is just having humans understand the code, and for that, using inheritance or not, the main solution is just to document what is going on.
Or, if the way around using inheritance is just having multiple copies of the same source code, then just document this fact so that when want to change one of the copies, likely change all the others, also. And, of course, generally should know all the places in the code base where that code, or some modification of it, is being used. Can solve such an issue with just documentation.
Or we don't want to look to programming language syntax and features to solve all problems of meaning in that software, meaning better communicated with documentation.
Gee, so far this is a polymorphic post since I didn't way what OOP language I'm using! So, I'm writing in Microsoft's Visual Basic .NET with their .NET Framework, ASP.NET (for Web pages), and ADO.NET (for using SQL Server). That Microsoft software is awash in classes, and that architecture seems to be working well.
Yes, that Microsoft code is awash in inheritance, but mostly I just ignore that fact and regard it as more just documentation than actual software. I get by mostly ignoring inheritance because I don't use it directly in my code.
In my project, the hard technical work was the applied math; it turns out, given the math, the corresponding code is simple.
But none of these quotes (and most dev thinking) seems to share my reasoning.
My problem: almost every argument for or against object orientation is about us, the developers. I rarely hear any arguments that consider our users or customers. Oh sure the usual (and lame), "It helps us serve them better."
I long ago lost track of all the lame bullshit (far too many to mention, but you know the culprits) that was supposed to revolutionize the way we build things without ever taking our users into consideration. Most of it was to make developers who couldn't build what was really needed appear as if they could. This has helped consulting firms and enterprise I.T. departments justify their rates and schedules, but has added little to the customers' benefit.
If the people who dream this shit up would stop focusing on what we need for 5 minutes and consider what they need, we'd all be way better off.
How has object orientation helped my customers? Frankly, I can't think of a thing. Add that quote to this list.
Java is a language right at the sweet spot on that continuum. Just advanced enough to prevent most stupid bugs but also mediocre enough that most devs can grok it.
In the words of one of its creators :
"We managed to drag them half way to Lisp"
And how would you program an enterprise Java App without object orientation ?
Read this and tell me that "crazy stuff like Monads" is an accurate statement:
http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/ba...
No. Why? Because it's bad. Not just prosaically, but because you haven't even bothered to make a point.
> [...] Most of it was to make developers who couldn't build what was really needed appear as if they could. This has helped consulting firms and enterprise I.T. departments justify their rates and schedules, but has added little to the customers' benefit.
It sounds like you think OO: 1) has been overblown to justify high salaries
> OO is the “structured programming” snake oil of the 90'
and 2) has added needless complexity
> “The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.”
See? Your point was there, it was just actually said in a substantive, humorous way.
I'd like to see a real case where something like this happened in code. It never happens to me.