As time goes on, my bafflement at our collective need to swear allegiance to our tools is turning into bewilderment. Our tools have objective, technical merits and flaws, but our love and hate for them is subjective and mostly useless. Cargo cult behavior is the real problem, and the endless bickering and correcting that articles like Joe's and this one contain only serve to perpetuate it.
I don't think the author was loving or hating OO, he was just pointing out the Joe Armstrong's points weren't very well argued. It's not bickering, it's discussion, and it helps people to understand the paradigms better.
Programming languages aren't just tools like hammers, they're sometimes also tools like railroads and AC mains power supplies. There you will be in a world of hurt if you want to pick different rail distances and voltages for each project.
In programming, if you lose in the popularity politics badly enough, your tool will die. Just ask the Dolphin SmallTalk or Lisp Machine programmers.
There are plenty of happy Lisp programmers and healthy tools for them around today. A rapidly increasing number, even, thanks to the likes of Clojure and maturing free CL implementations and ecosystems.
if you've ever had a job where your manager insisted that you must use technology XYZ, or that you were not allowed to use technology QPR, that's why people defend/attack approaches. nobody wants the industry to shift to a technology they find personally distasteful, lest you now have to base your career around it.
I really don't know why people seem to think that post is the final position of Joe Armstrong about OOP. It's a very old post, without any context, and since then he has gone on record praising Smalltalk and Alan Kay's take on objects many times:
I've noticed a pattern with people who rally against OO, that when you get down to the core of their argument and what irks them, they really just don't like Java, with all its superfluous-feeling ceremony code. Maybe try a lighter OO implementation first before you decry an entire methodology.
I'm sympathetic to this line of reasoning, for sure. I also see value in critiquing a methodology as it is expressed in practice. Java is one of the most popular languages.
I mean, you could critique C++, but at this point that seems kind of cruel. :)
Certainly that clouds it. I feel Java and the like also cloud people's judgement of static typing as well. Who wouldn't hate static typing when you have to write out these massive types all. over. the. place.
I've also encountered a lot of people who rally in favor of OO who don't really understand anything other than Java, so they think using objects is the only way to get polymorphism, information hiding, etc.
While I'm here, what would you recommend as "a lighter OO implementation"?
Because a lot of people have been exposed to Java, and a lot of people have been exposed to bad Java. There's not a lot of "good Java" (at least that I know of) that's publicly available for reading and understanding. Couple that with the infuriating environment (whoever came up with the 'application server' as exists in Java EE should just feel terrible about themselves) and it's easy to see why it gets people's hackles up.
Me, I don't mind Java so much, but the loads and loads of ceremonious bullshit that Java forces upon me is annoying and I absolutely despise web development in any of the existing tools[1]. I'd love something more friendly for what I do. (I would punt many adorable helpless kittens for a working C# on the JVM.)
[1] - Please don't reply to this to tell me I need to try Play. I have. It's not much better to work with, its developers are absentee and don't seem to want outside contributions, and it has no community to speak of. Not good.
That's definitely true, but when you say "Java", most people immediately think "Java EE", and that for things that should be simple (like a blog web app), you're forced into "enterprise" stuff.
It's largely cultural (and though they aren't doing a great job the Play guys should be commended for trying), but no less a problem.
Sometimes, if you want to put money on the table, you have to write in Java. (I'm in this boat. I don't hate Java, but I feel it's aggressively mediocre.)
IOW: not everybody is in charge of what technology they use. Further, not everybody has the resources nor the inclination to rearrange their entire life (and that of their dependents) in order to start their own company, all because of Java.
And sometimes it's just kibitzing or venting or what have you.
" It is not like a gun put on the head to make them use it."
From what I understand, a lot of universities do force students to use Java for many introductory courses, mine included (though we're migrating to Python.)
Exposure to different subjects is good, but there are better, cleaner languages to use for introducing concepts of programming such as variables, functions, algorithms, and data structures without taking up students' attention with syntax and boilerplate that has nothing to do with the lesson being taught.
Compare these hello world programs for instance:
Java:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
I think the author and Armstrong have significantly different ideas of what a "type" is. That's not a huge surprise—depending on background, you'll often get several definitions from different people.
One way to see types are things with fundamentally different access pattern: individual entities, sequentially retrieved ones (e.g. lists), fixed-length ordered lists whose elements are accessed positionally (e.g. tuples/vectors, fixed-length maps whose keys are known at compile-time/records/objects can also be viewed this way), unordered collections of distinct entities (sets), etc. I'd argue Armstrong's view is similar to this based on what he put in Erlang.
I can't tell what the author's view is, aside from "a class is a data type". This suggests that he equates a type with the set of functions that can be called on something and not have the compiler or runtime blow up (or perhaps, the set of functions that the programmer, by putting them in the same file, has explicitly said won't blow-up, whether or not that set is correct or complete). This throws fundamentally different things like lists, tuples, sets onto the same level as Person, Account, and PayrollRecord—which are often just short-hand names for tuples with different arities or names for their elements' positions.
If one has the later view, confusion over the point 3 is understandable: creating a new type for time is easy, just do class Time and define a bunch of properties and methods. In Armstrong's example, time is just a 6-tuple (or 3-tuple if all you want is hms). If you wanted to store it or send it over the wire, anything that could handle a tuple of integers would already know how to use it. If one function calls the last member "sec" and another "second", they're local names, so it doesn't matter as long as they're both accessing the same member.
One can object that it's easy to write serializers or that a compiler or library could figure it out, or that having different names for the same data elements indicates inconsistency in code that should be caught earlier. De gustibus non est disputandum. But, don't throw up your hands and say "this makes no sense!" without considering another perspective first.
But that's just because of bad programming, not because of OOP, I think. I mean, of course you can use OOP to make really crappy designs, but polymorphism should make you able to have N+M implementations and not M*N.
(Static) polymorphism is the very thing behind the STL. In order to decouple algorithms from data structures, you need a standard interface between them. In the case of the STL, that interface is iterators; other interfaces (such as stacks or ranges) are also possible, and in my experience much nicer to work with. But none of it requires OOP.
Well, of course OOP isn't required for a good design. What I'm saying is that, as far as not redefining functionality for different types, it doesn't work against it. At least not if we allow for static polymorphism, and I'm pretty sure that's considered an integral part of object orientation; at least I remember Kay talking about it.
Static polymorphism wasn’t part of OO originally; in Smalltalk, everything was late-bound. And you can of course write things generically even in the “classes and methods” kind of OO, but I think the culture around that paradigm encourages coding non-generically. You might say OO itself works toward genericity, but some OO-branded languages work against it.
A better "functions should be separate from objects" is for the fairly common case where there is a function that takes arguments of two (or more) different classes as arguments; which class should that belong to? It gets even worse when the function is commutative.
Isn't that what multimethods are for? Multimethod systems (like CLOS in Common Lisp) allow you to specialize methods based on the types of more than the first argument.
Once you have multimethods, you don't need to couple the method definitions to the class body. But you're still doing object oriented programming; CLOS is an object system. The fact that people assume that OOP==Java is unfortunate, but doesn't change the fact that CLOS is also OOP.
Beyond that, I think you're conflating how method bodies are placed in a file with "belonging to a class". I can define C++ methods outside of the class definition, but surely no one would say that such methods don't belong to a class.
Well if 95% of the population use a term to mean one thing, then arguing that no it really means something else doesn't get you anywhere.
And no, I'm not conflating those. In C++ and Java, classes are, among other things, namespaces. When you declare a method in a class, it ends up in that namespace. Where it is defined is unimportant.
When someone says "object oriented" unless they define their terms, or are clearly coming from the smalltalk school, I assume they are talking about C++/Java style OO, since that assumption is right the most often.
This is particularly true when someone is talking about "what's wrong" with OOP
Not sure where I sit on the issue, but I wanted to add this:
By putting the functions in the class definition, they become locked into that class. Other data structures could potentially make use of the function, but now they don't own it. I would say the alternative is a namespace, which you don't instantiate.
Armstrong's grief is the idea that the functionality for classes can then only come from direct lineage. As I understand it, though, the OO-heavy languages (Java, C#, probably others) lean much more heavily on interfaces now to mix in behavior, which gets away from that specific problem.
[EDIT] I forgot that interfaces don't give implementation, so the mixing is pretty limited.
Not sure what I think of the state issue, though. Too green on the functional stuff to say how else it should work. I would point out, though, that programs hide state from each other; couldn't objects just be the same thing on a smaller scale? (That is, an opaque interface with a few access points.) It really comes down to how well it's made in both cases.
Java doesn't have multiple inheritance, so if you want to avoid writing the same behavior twice you instead get to write several interfaces describing the ability to do the behaviors and write several functions in each class that implements such an interface (but does not contain the implementation) calling the functions defined in the interface on its pet InterfaceImpl.
This is probably a worthwhile tradeoff, but checking in hundreds of lines of code that do literally nothing is still a bad place to be.
This was running through my head as well, and means that your software design is bad. If you have such a useful function that you can translate it among classes, it should sit in some sort of static Util class. Think of it as the Swiss Army Knife you carry in your regular toolbox, a go-to tool, when you know you don't need anything specialized.
A lot of static methods on a Util object strikes me as a smell in the context of this discussion. In OO-land it's not a smell per se because often you have no alternative. But functionally your class is a glorified namespace.
Not necessarily. Of course defining classes is tricky, and there's no such thing as a rule of thumb, and that's more what I was trying to relay with my comment. And I agree with others who are saying that a piece of software where objects are crammed with tons of static classes is bad OOP.
I think in practice most OO programs end up with two general kinds of classes. Some are more for data structures, e.g. User or Address classes, and others are more for functions like IOWriter or DAOs.
But there aren't strict dividing lines between these two types, you end up mixing and matching functions and data as you see fit to help organize things. So you might put state into your function classes, such as the DB connection in a DAO:
db = DBGetter.get("serverAddress;dbName;username;password;")
userDao = new UserDao(db)
User user = userDao.getUserById(123)
You might also end up putting functions in your data classes, but you might not. As an example, to calculate the distance between two addresses you could put the function in the Address class:
float miles = address1.distanceTo(address2)
Or you could create a DistanceCalculator class that mostly just holds functions:
float miles = distanceCalculator.distanceBetween(address1, address2)
There are pros and cons to either design, personally I would go with the second one, but the point is that you have a choice. OO provides a flexible framework and there are many ways to design your classes. That's why there are a bunch of books on OO patterns and best practices, to help design the structure of classes. You can even work in a very functional/stateless style if you want to.
What OO gives you are tools for organizing your code but you can organize in many ways, some ways that help and some that hinder. As others have said, it's just a tool.
The use of ORMs should/does eliminate the need to write any of the data structure classes. They still exist but they're automatically generated and updated. And the use of your second implementation allows you to separate your state from your business logic.
> The basic idea of OO is that data and functions are bundled together, which is an idea that Armstrong seems to hold a grudge against (objection 1). But he gives no arguments as to why this is a bad idea.
Code reuse. Functions that are tied to a data type are less likely to be reused, and if you find that you want to reuse one it will, a lot of the time, require major refactoring (perhaps you have to move the function to a parent class, or create an entirely new class that represents the thing that is common between this data type's use and other data types' use of the function).
Whereas if your data and function are never coupled you actually wind up writing more generic functions that don't care about the type they are operating on.
Utility classes are code smell (paradigm smell, even). They exist where the OO model breaks down and you want to get-shit-done rather than spend an hour thinking about where you can stick function X so that it's useable by class Y and Z. Same goes for verb classes; you know, those does-the-thing classes that you create which are just objects wrapped around a single function called execute(), run(), create(), etc. etc.
Utility classes serve merely as organization purpose. It has nothing to do with OO. It's like putting related files in a directory, or putting related Python functions in a module.
I don't get how "stick function X so that it's useable by class Y and Z" is relevant for consideration. As the name suggested, utility classes are function libraries that can be used by all.
The problem is that what you "really" want when you're doing this is a plain old namespace. In some languages, objects necessarily come with a bunch of baggage and implications you don't want without some way to opt out.
Yeah it works fine most of the time, but it's a bit like the opposite of primitive obsession. You wouldn't use an Integer class for an index in a for loop.
In Java you have little choice but to dump it all in one place. Contrariwise, Python offers you more options; you can just make a module and put a bunch of top-level methods in there if you want. Go has some notion of "objects" but you can have methods or top-level functions in a given package. And so on.
No, the problem is not to use a namespace. The problem is I want to organize related functions as a group. Namespace is just one way to do it. There are other ways. Package, class, namespace, or module serve the same function in term of organizing functions.
What are the bunch of baggage and implication to use class as a way to organize functions?
I don't see the difference in usage you mentioned with Python module. Putting a bunch of "top-level" methods in a module is the same as putting a bunch of static methods in a class.
It is possible to do separate functions form data in OO languages.
It is less convenient than it is on other languages, where the wrapper class is not needed.
Separating functions from data is not the natural initial solution in an OO language, and is done as a refinement. It is not promoted as a default solution.
At some point the language is getting in the way and it better to go with something that supports what you are doing.
OO is a good fit for programming a lot of important types of software, such as GUI's or 3D graphics, where it is convenient to model the program data as consisting of widely different types of data structure, having a common interface, and which are easy to add new types to without having to change existing code.
Other than that, most use of OO is really just applying the language's OO structures to software idioms that exist outside OO, such as modules, or data hiding, or closures, and so forth.
The only times OO actively sucks is when you're trying to do non-OO stuff in a highly-OO language, such as trying to do algorithmic code in Java.
I think that most people that bash OO fail to understand how to use it properly.
From my experience many are also pretty bad at doing ADTs in module based languages.
OO has quite a few powerful concepts, and not all languages that are OO based explore the same set of concepts.
This is why the best languages to work with, are the ones which offer multi-paradigms, allowing the developers to pick the best abstractions for each scenario.
Any language that offers modules/packages as a way to organize code, regardless of which paradigms it supports.
This is why Modula-2 was named like that.
The original idea of ADT (Abstract Data Types) with modules, is that the module exports the public operations of your type along with its visible definition.
Then all operations on the ADT are done via the public operations.
This was the way of modular programming before OO became mainstream.
I don't get how state being evil. Pure functional program also has state. Its state are the arguments passed around between functions.
What people talk about the evilness of state really is the scope of the state. In a typical program, there could be global state in global variable, local state in function local variable, and the state passed around in function parameter. OO adds the instance level scope state for instance variables.
All these different levels of scoping are tools for programmers to manage the complexity in a program. If you don't like global scope, don't use it. If you don't like instance level scope, don't use it. Pure functional code are NOT appropriate 100% of the time. A global variable can be simpler and cleaner to get the job done. There are times for different tricks in a toolset. Use the right tool for the right problem.
edit: added the NOT. Bad omission completely changed the meaning.
State isn't so much evil as it introduces complexity. I think we can agree that complexity, or at least unnecessary complexity, is evil. :) The goal isn't so much to make people stop using state, but to make people think about it and question whether the complexity of state is, in fact, necessary. Maybe the answer is yes! But you should think about it, at least.
To expand on that wrt OO: OO tells you to encapsulate state in objects, generally, and then ignore it b/c abstraction, implementation detail, etc. But state makes it harder to reason about your program because now (ostensibly) the same function gives different answers depending on when you call it, not just how you call it. It couples your program that much more tightly to the order of operations. It introduces a bunch of assumptions dispersed implicitly throughout your code, as everything else now has to ensure that it does the right thing when the order of operations and/or input is correct vs not. That leads to bugs.
The counter-narrative is along the lines of what you suggest, which is to reduce state to a very small and limited scope, and put up a bunch of red flags around it. You say "these answers can change and this is the place to handle all the possibilities." You handle it in as few places as possible, and not laterally, everywhere else a method call chain or where there is state which depends on other state. Haskell is an example of this idea taken to the Nth degree.
But "the right tool for the right problem" isn't that helpful, although I think it's a worthwhile sentiment. The goal is to get people to think about the complexity they add to their programs, not to lambaste them for their choices or take away those choices in the first place.
I find it funny that programmers are debating why we should hate OO. It's like we know we should hate it but we are still in progress of figuring out why.
The title suggests that the aurhor will provide reasons for why OO actually is bad, but he doesn't. Why is it so fashionable to bash OO anyway? It's just a tool, it can be used wisely, but often it is not, which gives no reason to blame OO.
OO is not "just a tool", because it's often coupled with a language. A wrench is "just a tool" because I can always reach over for my socket set if that seems to fit better. Once I'm in Java, I am locked into a whole ecosystem and Java work hard to make sure you write OO code. I can't just reach for a lisp halfway through if I think that tool would work better.
IMHO, The biggest problem with the oft-repeated meme "just a tool" is the word "just". It dismisses out of hand the powerful ways in which tools (e.g. media) nudge us in certain directions and shape the way we work. Any time you hear someone say "just a tool", you are talking to someone who considers themselves cognitively separate from the tools they use, whereas in reality there is something much more fluid happening.
Let us not forget that tools themselves are designed, and some designs can be downright pathological. A kettle is a tool for making tea, but (in deference to Donald Norman) there is a reason we don't make kettles with the spout on the same side as the handle. Just because someone designed a tool to solve a particular problem does not mean that it's the best way to solve that problem. We can and should be critical of our tools without emotional attachment to them.
I disagree, as seems most commenters do. Sometimes OO is tightly coupled with an language (Java), and sometimes not (JavaScript), but that's just part of the consideration to be made before choosing which tool (language, OO/procedural) to select for the task at hand.
Once I'm in Java, I am locked into a whole ecosystem and Java work hard to make sure you write OO code. I can't just reach for a lisp halfway through if I think that tool would work better.
Yes, and I've done that before. However, there is still overhead in switching underlying data structures when switching between the two and making sure you don't break the STM among other things. It's hard to share data models between the two.
No, but I am saying that switching between them is not as easy as grabbing a different tool. The easiest place to switch languages is between abstraction layers or between functional components/processes. These language separations do not often coincide with the types of code that benefit from either OO style structure vs functional data processing.
This is a rather lazy argument I often see levied. It provides no real insight, and defends against everything equally. "Gotos are just a tool; On Error Resume Next is just a tool; null pointers are just a tool; etc."
That said, I think Joe's original article against OO was ill-informed (which is surprising, given his status), and this article rightly points out that Joe's arguments are bogus.
I don't think anyone disagrees that religious battles are stupid. But to sweep all discussion about tools under the "hey man, different tools for different jobs" just avoids critical thinking. If you don't want to participate in these discussions, don't. It's like a discussion on Windows Phone 7 UI, and someone comes along and says "lol, doesn't matter, Windows Phone 7 will never take off".
Just because it's one tool of many does not mean it's good. The whole tool thing is a bit of a non sequitur. After all Basic and Intercal are also just "a tool among others in the toolbox". This doesn't mean they are good tools!
I really do not understand why this tool argument comes up. Do you never evaluate the quality of your tools? I was planning to come up with a good analogy to some carpenter's tool that is objectively bad and not used any more, but then it turns out I know effectively nothing about carpentry.
Anyone participating in these types of discussions should start with a disclaimer whether they are talking about static typing, where types are largely equated with classes (aka "class-based" programming), or message passing, where types are largely orthogonal to classes.
The latter concept is what Alan Kay had in mind when he "invented" OO although arguably C++ and Java have really popularized OO as class-based programming.
Neither C nor PHP support functional programming well. They don't even acknowledge logic programming. But hey, you can use any style you want. As long as it's imperative.
I think the most multi-paradigm languages are certain lisps. As long as you only care about different declarative styles (basically the opposite of C), Haskell is surprisingly good: point-free programming is like stack-based programming and you can easily embed non-deterministic code as well. And you can write imperative code about as easily as writing functional code in PHP or C.