The author's objection to data encapsulation seems to be that for an object X, the internal state produced by "X.a(); X.b();" may be different from that produced by "X.b(); X.a();".
But this behavior is exactly what makes an object. Programmatic objects are supposed to be analogous to real-world objects. If I shift my car into first gear, then turn the key to start the engine, I get very different results than if I do the reverse.
Even abstract mathematical objects like vectors and matrices work this way. If I rotate a 3D vector twice, the final direction it points depends on the order I do the rotations in.
Your examples ignore the way that data encapsulation is used though. You can inspect the position of the gear lever in your car before deciding what to do next. A vector rotation relies on no external state whatsoever.
A better example would be if you had a counter hidden somewhere out of sight which counted the number of times you turned the key in your car, and introduced different behaviours for key-turning based upon that value.
> A better example would be if you had a counter hidden somewhere out of sight which counted the number of times you turned the key in your car, and introduced different behaviours for key-turning based upon that value.
Interestingly enough, every car does have something like that. (Think gas, maintenance, and repair.)
Your "better" example is almost exactly true of my car.
According to the dealer, the "Miles before service needed" that shows up when I start it is based on the number of cold starts, not just the number of miles actually driven.
I think this objection still misses the point. In my vector rotation example, a vector object's internal state is just three floating-point numbers. Even for this simple and well-understood object, "v.rotX( 30 ); v.rotY( 30 );" can give a different internal state than "v.rotY( 30 ); v.rotX( 30 );".
However, I do agree with you that it's easy for bad programmers to create objects that are counter-intuitive and difficult to understand. It's up to the programmer to insure that an object isn't encrusted with dozens of badly-thought-out methods with no obvious paradigm for how to use them.
When writing a program, you usually don't model the Platonic ideals that make up the universe, with perfect encapsulation. You model aspects of the world relevant to the problem you're solving. You give them shorthand names that make sense in the context of the program, not in the infinite real universe. A Bike class in an ecommerce app means something different than a Bike class in a bike-racing video game. Reusing the ecommerce class in the game context is a silly requirement.
Instead of "hiding", think of encapsulation as "organizing". You can organize verbs, most of the time, by the parts of program state they deal with. There are multiple reasonable ways to do it. That's what makes object modeling a more flexible tool than you give it credit for.
I think that's the point -- encapsulation is achievable in many non-OO ways, and encapsulation is basically just a subset of namespace management. In other words, encapsulation itself isn't necessarily bad, so griping about encapsulation doesn't seem like a very productive way to "prove" that OOP is "bad".
Hm, I'm not sure about that. The thrust of the argument seems to be that object orientation conflates state and behavior far beyond the point that it is useful--that things end up getting shoe-horned into objects where it doesn't make sense just to satisfy the paradigm. I think there's definitely something there even if the rant is a little over the top.
In my opinion OOP took off because it's such a natural fit for GUI programming and then everyone just started thinking that way. It's natural to think about data types and the functionality that goes along with them, but I don't think you need to go nearly so far down the path that recent languages have taken in order to satisfy that association.
My comment is not specifically about Java. I really think there is a limit to the usefulness of fusing all data to the code that operates on it. I'm not religious about it or anything, I think it's just something that bears consideration in today's software ecosystem.
I know it's popular around here to dislike OOP, but this guy's main objection is to mutable state. Analogous situations can occur in an imperative paradigm without objects.
You can easily make use of OOP with no mutable state. You don't get some of the features of OOP, but you can still leverage inheritance in interesting ways.
The other main objection is that OOP isn't a good model for all domains. Don't tell the OOP zealots I said this, but you don't have to use OOP when it doesn't model your problem well. Nobody forces you to use object-oriented techniques. Even if you're stuck with Java, you can just declare a bunch of static methods, and suddenly your "class" is a "module".
It's a tool, and like any other tool, it shouldn't hurt to have it in your box. Just remember to take it out only when it will help you.
> I know it's popular around here to dislike OOP, but this guy's main objection is to mutable state. Analogous situations can occur in an imperative paradigm without objects.
To be fair, the guy's one reference to imperative programming made it sound worse than OOP.
Imperative and object-oriented are on different axes. OO is an organizational paradigm, while imperative is an algorithmic paradigm. It's not hard to conceive of a more declarative object system.
I agree with some points and disagree with others here.
I think I see three underlying problems:
1) Mutable state has consequences, and OO happens to highlight some of them
2) The author would probably prefer generic functions, the "other way" to do OO IMHO, as they give you polymorphism while leaving the encapsulation issue to other mechansism, such as modules, as they mention.
3) OO examples are just terrible. Who on Earth writes a Bike class? I've seen whole arguments about whether Car should inherit from Vehicle and all this rubbish, all because people are taking vague and non-realistic examples, then getting tangled when they try and realise them. This is a road sure to lead to meaningless discussions and confusion on the parts of students. Instead, let's think about real software objects, such as files, windows, threads, and connections; and problem-domain objects such as users, orders and so on (as long as one is gently aware of the subtle issue that a 'user' is a person while the 'user' in the system is an object that models a person). OO can work well for modelling such objects.
The 'Bike' is a very common example used in OOP texts, hence why it was included in this discussion. I agree that it's kinda terrible, but this is the common textbook way that OO is described.
As stated in other posts here, the problem isn't so much the 'bike' example as the 'real world' context. Try using the bike example in a game context. You have to map controller buttons to bike.turnLeft(), bike.brake(), etc. These generic examples make a whole lot more sense in a valid context.
Terrible textbooks aren't evidence that object-orientation isn't a useful style. They're evidence that it's popular and not trivial to understand. That's evidence that it is useful. Your post is more evidence that it's popular (since you're bothering to write about it even though you don't like it) and not trivial to understand (since you didn't bother to mention any of the important aspects of it).
Who on Earth writes a Bike class? I've seen whole arguments about whether Car should inherit from Vehicle and all this rubbish, all because people are taking vague and non-realistic examples, then getting tangled when they try and realise them.
While I agree with your point, I don't think the problem has anything to do with Bike, Car and Vehicle as a choice of classes and that files, windows, threads and connections are guaranteed to be used any better. The problem is that people who use these classes as an example don't put them in the context of a concrete domain. I'm pretty sure that I would write a Bike (or Bicycle) class myself if I was developing a bicycle racing game. So the advice to the authors should be to choose a problem domain (hopefully something fun) and stick to it. Whether Car inherits from Vehicle or User implements Principal depends, ultimately, on the problem domain.
X.a() then X.b() may be completely different to X.b() then X.a(). This implied ordering is not really expressed anywhere.
Oh noz!
OO is capable of CORRECTLY modeling complicated real life objects like Rubik's Cubes, cars, clothes, and vending machines...
Why is it a failure that depositing $100 to a new checking account, then writing a $90 check does a different thing than writing a $90 check against an empty checking account, then depositing cash after the check has bounced?
I think the problem the author has (but fails to articulate well) is that the variance in consequences of doing things in different orders is not explicit. In short, it's like you are never allowed to know what the balance of your account is until you overdraw it.
Of course, I'm not sure whether that's a valid complaint either, since one can always define accessor/getter methods (e.g., "get_balance" for the checking account), but there are those who would argue that accessor methods violate the data protection principle of object oriented programming.
He's kinda missed the point of simplification you get from encapsulation. Encapsulation makes the API smaller and more manageable. If I want to ride a bike I don't want to have to worry about the internal workings. I just want to use the Pedal() method. Sure, if that throws a FallOffBikeException I might use more of the API to examine the problem. However I don't need an API that tells me everything, such as the chemical composition of the metal used in the frame.
For his gravity he also probably wants to outsource that to some sort of gravitatonal context that is supplied externally as opposed to including the physics in the bicycle. I'm pretty sure that in computer games they don't put the physics in the model code. :D
And btw, points in .NET (omg OOP!) are immutable structs so i'm not sure why he thinks everyone should make them a class.
I actually agree with the Author's issues around hidden mutable state. Luke Galea introduced me to the term "Candy Machine Interface" for this, where you keep pulling levers and pushing buttons and you have no idea how to get the candy you want out of the machine.
I also agree that the "real world" does this some times, but there is a difference between writing simulations of the real world and writing programs. I'm not disturbed by the idea of objects encapsulating hidden state, but rather the idea that objects have static, invariant interfaces.
One of the nicer ideas for dealing with this is to write explicit state machines and where (if your language allows) the interface actually changes as the state changes. If o.a(); o.b(); is different than o.b(); o.a();, you can write a state machine that makes this inspectable and explicit. If o.b() makes no sense until you call o.a(), you can simply not have o.b() be part of o's API/interface until you call o.a().
I won't suggest this is a general programming heuristic, but my experience in the last couple of years is that every business object modeling a real-world business entity ought to be implemented as a state machine until you discover a compelling reason to do something else. Ad-hoc state machines, like having the logic for whether o.b() does x or y based on the contents of fields _a and _b be embedded in the method b() are the stateful equivalent of spaghetti-coding with GOTO.
I credit Pete Forde for shaking me out of the complacent belief that I'm smart enough to embed everything in methods.
That's quite a nice idea! I certainly hadn't thought of that. I would upvote this several million times if I could.
Another thing that sometimes makes sense is Model-Based Testing (I was involved in the ModelJUnit project - http://www.cs.waikato.ac.nz/~marku/mbt/modeljunit/ - for example). It can sometimes catch these kinds of subtle API misuse issues.
Finally, I did some work on a tool that will statically analyse a program and that will attempt to ensure that action a() never occurs before action b(), for example. I wrote a blog post about it a few days ago:
So it's not like it's an insurmountable problem (people manage to produce software!), but I definitely like your suggestion of using explicit state machines whenever you insist on using mutable state.
rags, I wish you still wrote your blog. There was no better place to find mind-blowing stuff like that. You talk about different things because they're important, not because you were trying to be contrarian to get attention.
I find that a lot of my programming problems break down to a few of the same categories:
State manipulation
Data introspection
Data formats
Concurrent operations
Basically, things are hunky-dory so long as I have a simple data structure that every part of my program can use in the exact same way without destroying performance or entangling its operations with the rest of the program's state. It almost never quite works out like that for a non-trivial program. Hence I end up writing more complex data structures that can generate appropriate views and manipulations of data, state machines to formalize the order of operations and the resource management, and DSLs to generate functionality from data rather than repetitive code.
I think a big improvement would result just from building a convenient state machine syntax into the language.
This is a mind-blowing and cool idea as others have said, but the idea of an object whose methods change depending on their internal state is a bit counter-intuitive.
Real-world objects don't have this property -- it would be like the gas tank hatch disappearing from my car after I fill it up, and reappearing once I drive around for a minute.
Some do. When the gear shift lever on my Mazda is in one of the automatic slots, you can't upshift or downshift manually.
In business this is even more obvious. Contracts might have states: proposed, drafted, agreed, signed, and so forth. You can't sign a draft, it has no meaning. That's part of the business process.
Not quite the same. Your Mazda shift lever's manual slot doesn't disappear when you're in automatic. You can still sign a draft contract, it's just not binding.
In the software case you explained, the interface changes so you can't even try to do the wrong thing. On real-world objects, you can always try to do the wrong thing, it just doesn't work.
You cannot sign a draft, signing is a legal thing, not just a putting your name on a piece of paper thing. Imagine you're a manager and you call your EA: "Henderson, get me the YC contract to sign!"
Henderson replies back on the squack box: "Sorry sir, it's still a draft, it has to be approved by legal before you can sign it."
> Your Mazda shift lever's manual slot doesn't disappear when you're in automatic
No it doesn't, but you can't actually move the lever there without changing the gearbox's state from Drive to Manual.
So, we can say that it has an interface including the messages "+" and "-" at all times (The slot), but there's no way to send it a "+" or a "-" message unless the gearbox state is "M."
I think it's only counter-intuitive because it doesn't yet exist as a common programming paradigm. Once it does the error message should be no more confusing than average--harder to find than null exception, but easier than inspecting a core dump.
> Real-world objects don't have this property -- it would be like the gas tank hatch disappearing from my car after I fill it up, and reappearing once I drive around for a minute.
The entire point I was making in the article was that OOP is a terrible model for the real world. This is at odds with the claim made that the universe decomposes politely into objects and that OOP is a natural fit for modeling tasks.
I think an important thing to remember is that sometimes we don't want paradigms that correspond precisely to the real world. Airplanes didn't become a practical reality until we stopped trying to mimic the flapping actions of birds.
The suggestion of having a state machine driving the availability of actions seems tangential to the modeling task, and is instead related to correctness. Personally, I would take correctness over closeness-to-reality every time in modeling terms.
The smartness required to implement and/use a language feature which could catch some given error such as this at compile/parse time would be much greater than the smartest that today's OO has/requires.
I agree that encapsulation and full object-orientation are pretty much ideological - an object encapsulating what would otherwise be a two-member structure but with twenty members is an example of OO's horror. But if you avoid Bondage and Discipline Languages (java, c#), OO doesn't have to be that.
I've gone from Ruby back to c++ with Qt, and I find c++ nearly as good IF I let myself be comfortable with public member variable since I have access to easy, flexible list/hash object via Qt.
The simple approach - small objects that interface in an ad-hoc way with each PLUS big, well defined, flexible interfaces between real, functional divisions of the code. It does require a thought-out design separate from the immediate implementation but a little thinking is good. Not all the "big, upfront design" stuff was bad.
The author objects to a bicycle internally recording state that actually belongs to "the world". The problem with this criticism is that a bicycle is not a good example of something you'd model in a program. The abstraction was taken too literally.
I agree with the first comment, this is pure sketchy nonsense.
The bike example: If you only consider the bike to be some thing in the world, and only give it attributes based on the world-context, then yes, when you take the attributes predicated on that context out you are left with nothing.
But what about all the stuff you can say about a bike that isn't dependent on the world-context? Color, weight, tire pressure, the wear state of the breaks, how hard the brakes are being applied, the pressure being applied to the pedals, the rate of rotation of the gears and movement of the chain, what gear it is currently in, the state of the gear changing apparatus, the condition of the spokes, the wear level of the tires, whether it has dirt or mud splattered on it, what kind of seat it has, is it a girls or boys bike, a ten-speed or a bmx, if there is a current 'rider' of the bike, and if so a reference to the 'rider' instance.
Now, you can use these attributes in methods that allow you to interact with other objects in the environment. Suppose there is a 'rider' on the bike. The 'rider' can call methods like 'shift up' or 'push on pedal', and these methods might take arguments. 'shift up' probably doesn't take arguments, 'push on pedal' probably does, perhaps something like the pounds of force applied ot the pedal in the forward direction.
Now, the 'world' object still has to exist. Collision detection, orientation of objects in it, as well as speed must be tracked here. The world object would also have to notify objects of 'what they hear' or of other sensory data which must be applied to them. Finally, when 'press on pedal(5 pounds, 2 seconds)" is called, the bike must calculate - based on the current gear ratio, the 'in-ness' of the gear system (perhaps the chain is off etc), how oiled the chain and gears are - how much force is applied to the wheels, and the world would have to notify the bike in response how much resistance there is to that force, and somebody has to figure out what the final state is (for example, if you are off the ground, the wheel would spin freely. Only the world knows if you are on the ground, but only the bike knows how this effects the computation of wheels speed.)
In the end, nothing will be a very good approximation of the real world. In the real world, there are no objects, just amalgamation of particles whipping around or sticking together. What we perceive as objects are just big sticky clusters of particles, and even those particles aren't really objects, it's globs all the way down.
The important part is that you can make really cool stuff if you pick an appropriate approximation to model.
OOP is not something to do with real world objects. It's just told as this way. OOP is about encapsulating "data objects", related data and programs in close contact. Extending this brings us todays OOP concepts. But it is just data bind togeter.
Giving real world objects as examples hides details. So thinking as physical objects is a bad behavior in this context.
This blog is currently under review due to possible Blogger Terms of Service violations.
If you're a regular reader of this blog and are confident that the content is appropriate, feel free to click "Proceed" to proceed to the blog. We apologize for the inconvenience.
If you're an author of this blog, please follow the instructions on your dashboard for removing this warning page.
Apparently blogger's automatic spam-detection system didn't like the flood of traffic that suddenly hit the blog as a result of this article making the front page. I've requested it be fixed, so hopefully that message won't stay there for long.
I agree with the general conclusion (as I understand it) that mutable objects aren't a good way of modeling state, but the case has been made much more strongly and more clearly elsewhere, such as here:
You can do state and concurrency correctly in an OO language with locks, but it isn't practical. It gets exponentially more difficult as a project grows, and realistically any human being is going to end up with something that only almost always works correctly, with the occasional failures that are left over almost impossible to find.
1. The OO model of C++ and Java (the languages the author has experience with) is not the only model of OO around.
2. State has to be maintained somewhere in the program. While functional languages encourage minimizing state, nothing prevents the programmer from doing the same in an OO language.
If you want to argue that functional programming is better than object-oriented programming, you should take an object-oriented program, show how you would rewrite it as a functional program, show us why the result is better, and then show how the defects in the OO program are unavoidable within OO.
Some aspects of "goodness" for programs are:
* Brevity.
* Whether the program does something useful or at least entertaining.
* Local modifiability: that some modification of the program requires only local knowledge and local changes, rather than global knowledge and global changes. The more local, the better. This is conditional, though: requiring a ten-line change to a ten-line program is still better than requiring a twenty-line change to an eighty-line program. Demonstrating this requires that you show what is needed to make some particular change to both versions.
* Speed.
* Memory usage.
* Readability.
If you can show that your rewrite is better at least one of these axes and no worse on any of them, and that the difference is a result of OO vs. functional, then you will have shown that OO is worse than functional style for that program. This shouldn't be hard; I think symbolic differentiation is the usual example program for this. If it's better on some but worse on others, then you've shown that for that program, the choice is a situationally-dependent tradeoff.
If you can show that this is true for a wide range of programs, then you will have shown that OO is worse than functional in general.
However, you haven't shown it even for one program. Instead you have some vague bullshit example from some terrible textbook, some vague bullshit example from a sci-fi movie, some theoretical objections stated in the form of a series of mutually contradictory statements, a statement that you aren't actually an idiot (in case we might think that from reading the rest of your article, I guess?), a bunch of apparently false statements about Standard ML, and a bunch of evidence that you're pretty much missing the point on OOP.
Edit: Aristotle Pagaltzis wrote an excellent takedown of what Gian is missing a few years ago: http://plasmasturm.org/log/340/
I still haven't seen the non-shitty OO tutorial he was wishing for.
I think you're reading wayyyy to far into this. I wasn't making a this-versus-that argument. I was having a rant about X being totally unfit for the purpose for which it is sold (in this case, X is 'OOP' and its purpose is 'modeling systems in an intuitive, natural way that corresponds to the real world'). I even included the word "rant" in the first few sentences just so that people who don't like reading rants could close the window at that point.
So basically, I respect that you disagree, but complaining that something that was clearly labeled as a rant didn't present objective evidence is asking a little too much.
The context in which modeling systems in a way that corresponds to the real world makes sense is when you're writing a simulation of the real world. The Sims, say, or Knuth's elevator example. Historically this is where OO came from. It was only later (with Smalltalk, for which the term "OO" was invented) that it was used for more general programming.
My complaint wasn't that it was a rant. It was that it was a vacuous, boring rant.
You are being way too aggressive for no discernible reason.
Aside from that, I believe you left out the most important aspect of functional programming: correctness. If you take a pure language like Haskell you're going to have to jump through some hoops, but the point is to create a more correct program. The benefits of that are not going to be seen in a comparison of the final product.
In kragen's defense, we do know each other (from the internet), and we argue about these sorts of things to no end.
So there is a discernible reason, it's just probably not obvious to the casual observer :)
All of these properties of functional programming are true and nice, but that still ignores the fact that I wasn't making an ideological point about FP vs OOP! I said "here is a rant about why I don't like OOP as a modeling paradigm" and then (as a separate point) "here are a bunch of ways in which this other paradigm I'm familiar with achieves the same things as OOP without annoying the pants off me". It's a subtle distinction, but I wouldn't want to be thought of as just a run-of-the-mill religious-war troll.
The "missing the point" argument is a little bit easy. It's not that I don't understand OOP - I do, and I've done far too much of it. I understand it, and I reject it as a useful paradigm for any of the classes of program that I would like to write. That's an easy "out", right? Whereby it's easy to say "oh, you obviously don't write programs of type x", but I have enough confidence in my experience doing both general-purpose commercial programming and very specialist research programming to think that I can at least make that judgement on the basis of a reasonably large set of problem domains. Perhaps there are domains that match OOP really well. I just haven't found them yet.
And I'm not sure where you are getting most of these criticisms. Did you actually read to the end?
53 comments
[ 8.0 ms ] story [ 117 ms ] threadBut this behavior is exactly what makes an object. Programmatic objects are supposed to be analogous to real-world objects. If I shift my car into first gear, then turn the key to start the engine, I get very different results than if I do the reverse.
Even abstract mathematical objects like vectors and matrices work this way. If I rotate a 3D vector twice, the final direction it points depends on the order I do the rotations in.
A better example would be if you had a counter hidden somewhere out of sight which counted the number of times you turned the key in your car, and introduced different behaviours for key-turning based upon that value.
Interestingly enough, every car does have something like that. (Think gas, maintenance, and repair.)
The real world does have mutable state.
According to the dealer, the "Miles before service needed" that shows up when I start it is based on the number of cold starts, not just the number of miles actually driven.
However, I do agree with you that it's easy for bad programmers to create objects that are counter-intuitive and difficult to understand. It's up to the programmer to insure that an object isn't encrusted with dozens of badly-thought-out methods with no obvious paradigm for how to use them.
Instead of "hiding", think of encapsulation as "organizing". You can organize verbs, most of the time, by the parts of program state they deal with. There are multiple reasonable ways to do it. That's what makes object modeling a more flexible tool than you give it credit for.
Isn't that reinforcing the point that the main way that OOP is useful is for name-space control, which is achievable in many non-OO ways?
In my opinion OOP took off because it's such a natural fit for GUI programming and then everyone just started thinking that way. It's natural to think about data types and the functionality that goes along with them, but I don't think you need to go nearly so far down the path that recent languages have taken in order to satisfy that association.
You can easily make use of OOP with no mutable state. You don't get some of the features of OOP, but you can still leverage inheritance in interesting ways.
The other main objection is that OOP isn't a good model for all domains. Don't tell the OOP zealots I said this, but you don't have to use OOP when it doesn't model your problem well. Nobody forces you to use object-oriented techniques. Even if you're stuck with Java, you can just declare a bunch of static methods, and suddenly your "class" is a "module".
It's a tool, and like any other tool, it shouldn't hurt to have it in your box. Just remember to take it out only when it will help you.
To be fair, the guy's one reference to imperative programming made it sound worse than OOP.
I think I see three underlying problems:
1) Mutable state has consequences, and OO happens to highlight some of them
2) The author would probably prefer generic functions, the "other way" to do OO IMHO, as they give you polymorphism while leaving the encapsulation issue to other mechansism, such as modules, as they mention.
3) OO examples are just terrible. Who on Earth writes a Bike class? I've seen whole arguments about whether Car should inherit from Vehicle and all this rubbish, all because people are taking vague and non-realistic examples, then getting tangled when they try and realise them. This is a road sure to lead to meaningless discussions and confusion on the parts of students. Instead, let's think about real software objects, such as files, windows, threads, and connections; and problem-domain objects such as users, orders and so on (as long as one is gently aware of the subtle issue that a 'user' is a person while the 'user' in the system is an object that models a person). OO can work well for modelling such objects.
http://java.sun.com/docs/books/tutorial/java/concepts/inheri...
http://www.novio.be/blog/?p=621
http://www.roseindia.net/software-tutorials/detail/9172
The 'Bike' is a very common example used in OOP texts, hence why it was included in this discussion. I agree that it's kinda terrible, but this is the common textbook way that OO is described.
While I agree with your point, I don't think the problem has anything to do with Bike, Car and Vehicle as a choice of classes and that files, windows, threads and connections are guaranteed to be used any better. The problem is that people who use these classes as an example don't put them in the context of a concrete domain. I'm pretty sure that I would write a Bike (or Bicycle) class myself if I was developing a bicycle racing game. So the advice to the authors should be to choose a problem domain (hopefully something fun) and stick to it. Whether Car inherits from Vehicle or User implements Principal depends, ultimately, on the problem domain.
Oh noz!
OO is capable of CORRECTLY modeling complicated real life objects like Rubik's Cubes, cars, clothes, and vending machines...
Why is it a failure that depositing $100 to a new checking account, then writing a $90 check does a different thing than writing a $90 check against an empty checking account, then depositing cash after the check has bounced?
Of course, I'm not sure whether that's a valid complaint either, since one can always define accessor/getter methods (e.g., "get_balance" for the checking account), but there are those who would argue that accessor methods violate the data protection principle of object oriented programming.
And btw, points in .NET (omg OOP!) are immutable structs so i'm not sure why he thinks everyone should make them a class.
I also agree that the "real world" does this some times, but there is a difference between writing simulations of the real world and writing programs. I'm not disturbed by the idea of objects encapsulating hidden state, but rather the idea that objects have static, invariant interfaces.
One of the nicer ideas for dealing with this is to write explicit state machines and where (if your language allows) the interface actually changes as the state changes. If o.a(); o.b(); is different than o.b(); o.a();, you can write a state machine that makes this inspectable and explicit. If o.b() makes no sense until you call o.a(), you can simply not have o.b() be part of o's API/interface until you call o.a().
I won't suggest this is a general programming heuristic, but my experience in the last couple of years is that every business object modeling a real-world business entity ought to be implemented as a state machine until you discover a compelling reason to do something else. Ad-hoc state machines, like having the logic for whether o.b() does x or y based on the contents of fields _a and _b be embedded in the method b() are the stateful equivalent of spaghetti-coding with GOTO.
I credit Pete Forde for shaking me out of the complacent belief that I'm smart enough to embed everything in methods.
Another thing that sometimes makes sense is Model-Based Testing (I was involved in the ModelJUnit project - http://www.cs.waikato.ac.nz/~marku/mbt/modeljunit/ - for example). It can sometimes catch these kinds of subtle API misuse issues.
Finally, I did some work on a tool that will statically analyse a program and that will attempt to ensure that action a() never occurs before action b(), for example. I wrote a blog post about it a few days ago:
http://www.plsadventures.com/2009/08/prototype-static-analys...
So it's not like it's an insurmountable problem (people manage to produce software!), but I definitely like your suggestion of using explicit state machines whenever you insist on using mutable state.
State manipulation Data introspection Data formats Concurrent operations
Basically, things are hunky-dory so long as I have a simple data structure that every part of my program can use in the exact same way without destroying performance or entangling its operations with the rest of the program's state. It almost never quite works out like that for a non-trivial program. Hence I end up writing more complex data structures that can generate appropriate views and manipulations of data, state machines to formalize the order of operations and the resource management, and DSLs to generate functionality from data rather than repetitive code.
I think a big improvement would result just from building a convenient state machine syntax into the language.
Real-world objects don't have this property -- it would be like the gas tank hatch disappearing from my car after I fill it up, and reappearing once I drive around for a minute.
Some do. When the gear shift lever on my Mazda is in one of the automatic slots, you can't upshift or downshift manually.
In business this is even more obvious. Contracts might have states: proposed, drafted, agreed, signed, and so forth. You can't sign a draft, it has no meaning. That's part of the business process.
In the software case you explained, the interface changes so you can't even try to do the wrong thing. On real-world objects, you can always try to do the wrong thing, it just doesn't work.
Henderson replies back on the squack box: "Sorry sir, it's still a draft, it has to be approved by legal before you can sign it."
> Your Mazda shift lever's manual slot doesn't disappear when you're in automatic
No it doesn't, but you can't actually move the lever there without changing the gearbox's state from Drive to Manual.
So, we can say that it has an interface including the messages "+" and "-" at all times (The slot), but there's no way to send it a "+" or a "-" message unless the gearbox state is "M."
The entire point I was making in the article was that OOP is a terrible model for the real world. This is at odds with the claim made that the universe decomposes politely into objects and that OOP is a natural fit for modeling tasks.
I think an important thing to remember is that sometimes we don't want paradigms that correspond precisely to the real world. Airplanes didn't become a practical reality until we stopped trying to mimic the flapping actions of birds.
The suggestion of having a state machine driving the availability of actions seems tangential to the modeling task, and is instead related to correctness. Personally, I would take correctness over closeness-to-reality every time in modeling terms.
I agree that encapsulation and full object-orientation are pretty much ideological - an object encapsulating what would otherwise be a two-member structure but with twenty members is an example of OO's horror. But if you avoid Bondage and Discipline Languages (java, c#), OO doesn't have to be that.
I've gone from Ruby back to c++ with Qt, and I find c++ nearly as good IF I let myself be comfortable with public member variable since I have access to easy, flexible list/hash object via Qt.
The simple approach - small objects that interface in an ad-hoc way with each PLUS big, well defined, flexible interfaces between real, functional divisions of the code. It does require a thought-out design separate from the immediate implementation but a little thinking is good. Not all the "big, upfront design" stuff was bad.
Reminds me of: If you have a procedure with ten parameters, you probably missed some.
http://www.cs.yale.edu/quotes.html
The bike example: If you only consider the bike to be some thing in the world, and only give it attributes based on the world-context, then yes, when you take the attributes predicated on that context out you are left with nothing.
But what about all the stuff you can say about a bike that isn't dependent on the world-context? Color, weight, tire pressure, the wear state of the breaks, how hard the brakes are being applied, the pressure being applied to the pedals, the rate of rotation of the gears and movement of the chain, what gear it is currently in, the state of the gear changing apparatus, the condition of the spokes, the wear level of the tires, whether it has dirt or mud splattered on it, what kind of seat it has, is it a girls or boys bike, a ten-speed or a bmx, if there is a current 'rider' of the bike, and if so a reference to the 'rider' instance.
Now, you can use these attributes in methods that allow you to interact with other objects in the environment. Suppose there is a 'rider' on the bike. The 'rider' can call methods like 'shift up' or 'push on pedal', and these methods might take arguments. 'shift up' probably doesn't take arguments, 'push on pedal' probably does, perhaps something like the pounds of force applied ot the pedal in the forward direction.
Now, the 'world' object still has to exist. Collision detection, orientation of objects in it, as well as speed must be tracked here. The world object would also have to notify objects of 'what they hear' or of other sensory data which must be applied to them. Finally, when 'press on pedal(5 pounds, 2 seconds)" is called, the bike must calculate - based on the current gear ratio, the 'in-ness' of the gear system (perhaps the chain is off etc), how oiled the chain and gears are - how much force is applied to the wheels, and the world would have to notify the bike in response how much resistance there is to that force, and somebody has to figure out what the final state is (for example, if you are off the ground, the wheel would spin freely. Only the world knows if you are on the ground, but only the bike knows how this effects the computation of wheels speed.)
In the end, nothing will be a very good approximation of the real world. In the real world, there are no objects, just amalgamation of particles whipping around or sticking together. What we perceive as objects are just big sticky clusters of particles, and even those particles aren't really objects, it's globs all the way down.
The important part is that you can make really cool stuff if you pick an appropriate approximation to model.
Giving real world objects as examples hides details. So thinking as physical objects is a bad behavior in this context.
Possible Blogger Terms of Service Violations
This blog is currently under review due to possible Blogger Terms of Service violations.
If you're a regular reader of this blog and are confident that the content is appropriate, feel free to click "Proceed" to proceed to the blog. We apologize for the inconvenience.
If you're an author of this blog, please follow the instructions on your dashboard for removing this warning page.
http://clojure.blip.tv/file/812787/
You can do state and concurrency correctly in an OO language with locks, but it isn't practical. It gets exponentially more difficult as a project grows, and realistically any human being is going to end up with something that only almost always works correctly, with the occasional failures that are left over almost impossible to find.
1. The OO model of C++ and Java (the languages the author has experience with) is not the only model of OO around.
2. State has to be maintained somewhere in the program. While functional languages encourage minimizing state, nothing prevents the programmer from doing the same in an OO language.
3. Modeling is hard, regardless of the PL.
http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy...
http://www.gamedev.net/community/forums/topic.asp?topic_id=4...
http://www.gamedev.net/community/forums/topic.asp?topic_id=4...
http://didntread.wordpress.com/2009/07/14/building-a-stackle...
If you want to argue that functional programming is better than object-oriented programming, you should take an object-oriented program, show how you would rewrite it as a functional program, show us why the result is better, and then show how the defects in the OO program are unavoidable within OO.
Some aspects of "goodness" for programs are:
* Brevity.
* Whether the program does something useful or at least entertaining.
* Local modifiability: that some modification of the program requires only local knowledge and local changes, rather than global knowledge and global changes. The more local, the better. This is conditional, though: requiring a ten-line change to a ten-line program is still better than requiring a twenty-line change to an eighty-line program. Demonstrating this requires that you show what is needed to make some particular change to both versions.
* Speed.
* Memory usage.
* Readability.
If you can show that your rewrite is better at least one of these axes and no worse on any of them, and that the difference is a result of OO vs. functional, then you will have shown that OO is worse than functional style for that program. This shouldn't be hard; I think symbolic differentiation is the usual example program for this. If it's better on some but worse on others, then you've shown that for that program, the choice is a situationally-dependent tradeoff.
If you can show that this is true for a wide range of programs, then you will have shown that OO is worse than functional in general.
However, you haven't shown it even for one program. Instead you have some vague bullshit example from some terrible textbook, some vague bullshit example from a sci-fi movie, some theoretical objections stated in the form of a series of mutually contradictory statements, a statement that you aren't actually an idiot (in case we might think that from reading the rest of your article, I guess?), a bunch of apparently false statements about Standard ML, and a bunch of evidence that you're pretty much missing the point on OOP.
Edit: Aristotle Pagaltzis wrote an excellent takedown of what Gian is missing a few years ago: http://plasmasturm.org/log/340/
I still haven't seen the non-shitty OO tutorial he was wishing for.
So basically, I respect that you disagree, but complaining that something that was clearly labeled as a rant didn't present objective evidence is asking a little too much.
My complaint wasn't that it was a rant. It was that it was a vacuous, boring rant.
Aside from that, I believe you left out the most important aspect of functional programming: correctness. If you take a pure language like Haskell you're going to have to jump through some hoops, but the point is to create a more correct program. The benefits of that are not going to be seen in a comparison of the final product.
So there is a discernible reason, it's just probably not obvious to the casual observer :)
All of these properties of functional programming are true and nice, but that still ignores the fact that I wasn't making an ideological point about FP vs OOP! I said "here is a rant about why I don't like OOP as a modeling paradigm" and then (as a separate point) "here are a bunch of ways in which this other paradigm I'm familiar with achieves the same things as OOP without annoying the pants off me". It's a subtle distinction, but I wouldn't want to be thought of as just a run-of-the-mill religious-war troll.
And I'm not sure where you are getting most of these criticisms. Did you actually read to the end?