Hacker News new | ask | show | jobs
by weland 4607 days ago
This article is remarkably low on arguments, despite being so long. Just a few of its gems:

> OOP is wrong because of its definition of an “object”, and its attempt of trying to fit everything into it. When that goes to the extreme, you arrive at the notion “everything is an object“. But this notion is wrong, because: > There exists things that are not objects. Functions are not objects.

This has two problems:

1. It's factually wrong. Smalltalk functions are first-class objects.

2. It fails to explain why that would be wrong (assuming it were, you know, true).

The author does give the (poor) examples of Python and Scala, which -- in his opinion -- incorrectly call a function an object, because only the __call__ and apply are the "function objects" one is trying to define, but they were "kidnapped" and jailed into their wrapping objects. IMO, this is ontologically incorrect. The same can be said of any object's definition: if I have an object called PlaneVector, the "vector object" itself would actually be only its x and y properties, which I have kidnapped and jailed into my object.

The fact that this structure (the object I'm defining in my programming language) is not the same as its counterpart in the world of ideas (i.e. the plane vector in maths) should be fairly obvious, despite using the same generic term (i.e. object) to denote both.

Behold, then, the straw man:

> Most OO languages also lack correct implementations of first-class functions. An extreme is Java, which doesn’t allow functions to be passed as data at all. You can always wrap functions into objects and call them “methods”, but as I said, that’s kidnapping. The lack of first-class functions is the major reason why there are so many “design patterns” in Java. Once you have first-class functions, you will need almost none of the design patterns.

There are a lot of things wrong with Java, of course, which does not mean that they are also issue of object-oriented programming. Inheritance (sorry...) doesn't work both ways: the fact that a Lexus is prohibitively expensive doesn't mean moving vehicles are prohibitively expensive.

I have less of a problem with the author's treatment of functional programming. What he's essentially trying to argue is that:

> Simulating them [side effects] with pure functions is doomed to be inefficient, complicated and even ugly. Have you noticed how easy it is to implement circular data structures or random number generators in C? The same is not true for Haskell.

IMO, this is true, but the isolation of side effects doesn't have to happen only by the ugly means he outlines next (i.e. monads) in every application. A typical CRUD application the web kids like to write can be written so that the only side effects involved are in fact modifications of the database, in which case the side effects are confined to SQL.

I also think there's an ontological error in this argument. Its foundation is that FP tries to "ignore" side effects even though they are real, but the examples the author gives are several layers of abstraction below. C also woefully ignores some of the side effects in the silicon (e.g. if you go low enough, calling a function with no side effects that does not even alter the state of the program will, in fact, alter the state of the underlying silicon, and quite substantially so), and I think it's reasonable to assume that we can't quite move the debate into which of those should be ignored or not.

All languages abstract the silicon away, and it's not only good that they do, it's what they were built for. I honestly don't miss hardwiring relays.

7 comments

>> There are a lot of things wrong with Java, of course, which does not mean that they are also issue of object-oriented programming. Inheritance (sorry...) doesn't work both ways: the fact that a Lexus is prohibitively expensive doesn't mean moving vehicles are prohibitively expensive.

But isn't the main point of the article that OOP just doesn't always fit the problem domain, that sometimes you want to model something in a way where forcing it into objects (nouns and verbs) only adds complexity without benefits?

I don't think the article condemns OOP in any way except when it becomes a religious dogma that everything needs to be an object, as it is in Java. I've written many different types of programs in various languages, and I can only agree that sometimes, OOP simply isn't what you are looking for. Think about streaming data processing for example, or something reactive like a network service. Surely some parts of the outside interface or the 'glue' between the outside interface and the actual number crunching or request processing can be modeled using OOP, but at the core, there's all kinds of processing on blocks of data or asynchronous IO going on that doesn't need objects and methods, and doesn't add to code clarity or stability in anyway (on the contrary even).

The article basically perfectly describes why I very much prefer programming in Python or C++/Objective-C over Java: they allow me to mix and match different programming paradigms for different problems.

> I don't think the article condemns OOP in any way except when it becomes a religious dogma that everything needs to be an object, as it is in Java.

But Java doesn't make everything an object. The obvious one is that 'primitive types' aren't objects (int, bool, float, etc.), but also Java has hard-wired control structures (for, while, if, try, etc.) which aren't OO (compare to Smalltalk, for example; where "ifTrue" is a method of boolean objects, "each" is a method of collection objects and "times" is a method of number objects). Also its classes and methods aren't objects, like they are in Smalltalk, Python, etc.

I would argue that that Python's first-class classes and first-class functions/methods makes it much more 'religiously OO' than Java.

Just because all code must be in a class, doesn't mean that everything in Java is an object. I don't know why people keep saying that.

My favorite in Java are String instances, which are objects, but adding them with + is not a method call, because Strings are also primitives, sort of, hence + is in fact either eliminated (if adding two constant values) or the code gets translated to a concatenation powered by an ad-hoc StringBuilder instance. The JVM doesn't even pretend that a static method was invoked. It simply pukes some bytecode.

For example there is no interface in Java that you can use to add stuff, because from the language's point of view, adding integers is totally different from adding strings which is also totally different from adding BigIntegers, which is totally different from adding BigDecimals. Well, to be pedantic, Strings addition is in fact concatenation, as Strings are in fact Lists of Chars. I know, not fair to put them in the same bucket.

But, but, wait for it - BigDecimal and BigInteger actually do implement a Number class. But apparently Numbers in Java's vision are not even monoids, let alone rings (because duh, you also have multiplication). Let's not even mention that all real numbers have natural ordering. Like seriously, how can one get this so wrong?

Coupled with the explicit typing needed and the lack of type-classes or anything similar, it means that you simply can't write functions that operate on Numbers, as there is no such thing and you can't implement it by yourself.

People that bitch about Java being too OOP, missed a couple of lessons on their way to enlightenment.

Just once I'd like to see someone lament the "not everything should have to be an object" using as an example a language where everything actually is an object.
> But isn't the main point of the article that OOP just doesn't always fit the problem domain, that sometimes you want to model something in a way where forcing it into objects (nouns and verbs) only adds complexity without benefits?

Perhaps that's the point of the article, but the fact that a programmer might want to use a paradigm in an unfit manner is arguably not a problem of the paradigm.

OOP is fairly foreign to my daily work, so I'm not too attached to it, but I think a lot of the criticism it receives is unfair. It gets a lot of crap because Java and C++ implement it incompletely (and the part that they do implement is done quite poorly), and people naturally think it's a problem of the paradigm itself. I think 90% of the "OOP is bad because..." arguments are routinely handled with "Yeah, Smalltalk actually solves that by..." and should actually be phrased as "Java is bad because...".

> not a problem of the paradigm

Correct. The main problem I see with OOP is that some people advocate it as the only correct way to write maintainable code, which, of course is completely wrong. In fact, OOP doesn't translate well to some problems. There are also problems where FP doesn't work as nicely as other paradigms. In short, there is no silver bullet and advocates of any paradigm should be more open about that.

> Correct. The main problem I see with OOP is that some people advocate it as the only correct way to write maintainable code, which, of course is completely wrong.

Yes, my opinion is similar. There are problems that naturally lend themselves to being modeled using objects, and others which have to be beaten into submission. Not having to beat them into submission with half-witted OOP implementations, like C++, does help, but it is not always sufficient to take away the sensation of "unnaturalness", if you don't mind the invented word.

Everything in Java is not an object, and the author never made this claim. Languages he referenced where everything is an object are Python and Scala.
Searching for the main point, when the arguments brought forth to support the conclusion are wrong, does not make sense.

From elementary mathematics, A => B only makes sense when A is true, because falsity implies anything. And I know that productivity and all the other traits that matter haven't been proven formally for the paradigms we talk about, but in our flame-wars we could at least pretend to be scientists.

> A typical CRUD application the web kids like to write can be written so that the only side effects involved are in fact modifications of the database, in which case the side effects are confined to SQL.

Well, writing the response is a side effect as well, so no. But the business logic itself can (usually) be pure, even if it's sandwiched between effectful layers (web and SQL). And looking at how a well-crafted CRUD application is architectured, the business layer is usually made up of singleton services holding a reference to a few persistence-related singletons, nothing that could not easily be made functional.

Indeed; I'm used to writing systems software, so user interaction tends to elude me :).

That being said, I would dare say that the business layer is the one which would benefit the most from a FP perspective. I can think of dozens of bugs in my code that originated in my inability to correctly keep track of what was otherwise needlessly exposed state.

Being many layers closer to the silicon, I don't actually use any functional programming language for my work (it's C and Assembly all the way...), so I can't speak for using one. But applying some functional framework to my code proved immensely useful once I started doing it.

(At the risk of sounding like a hipster, that was actually before FP suddenly becoming cool. I tried learning Haskell once and miserably failed; I was only smart enough to learn some Common Lisp).

Indeed, many languages would benefit simply from having modifiers that reverse the typical use of "const". Make everything immutable / side-effect-free by default, and add a "mother may I" keyword that allows mutation / side-effects without giving a compile error.

Hopefully, programmers would learn to avoid writing code that requires the "stomps-all-over-shit" keyword except when they really did need it. (assuming tail call elimination for simple loops)

One of D's improvements upon C/C++ is its ability to enforce "deep const-ness".
Well, that's for theory. In practice I have yet to see a big CRUD like Facebook rewritten functional style, and would very interested to check how this magically dissolve the inherent complexity of such a beast.

Until then, I'd continue to think that functional is shiny and sexy but that the proven and pragmatic way to keep complexity in check is still the good old and boring oop way.

As other commenters have said the point isn't the eliminate complexity. Some problems are actually inherently or irreducibly complex! The goal is to find ways to manage the complexity. Abstraction barriers are a trivial example[0]. Type systems are another approach, hopefully foisting the complex management of datatype correctness on the compiler rather than the programmer.

None of these approaches necessarily make the problem itself less complex. But as with a type system there are ways in which a language can help, or hinder. A language which adds too much complexity incidental to the problem you're trying to solve is an example. TFA is groping towards this point, but they did a poor job arguing it.

The proposition is that a functional language's abstractions, conventions, and patterns may make it easier to manage (not dissolve) complexity. It's hard to argue that (e.g.) functions which are referentially transparent are easier to reason about, for instance. Whether this is more difficult in the large, as a project scales, is unknown to me.

[0]: http://mitpress.mit.edu/sicp/full-text/sicp/book/node29.html

I don't think anybody is claiming that functional programming makes complexity magically go away. The claim is that functional programming (especially when backed by a strong static type system) makes several categories of issues essentially disappear. That's a much more reasonable claim to make.
And a very powerful property. Never mind conserving CPU or memory; the slowest component in any system is the wetware programming the system: you. I always try to use tools and techniques that eliminate the possibility of categories of bugs. I use scope to control use-counting when possible; I use as strong a type as is helpful in the tool I'm using etc.
This article's thesis is pretty much "both object-oriented and functional paradigms have limitations which become apparent when you push them to ridiculous extremes". Which is neither an interesting thesis nor one that is in dispute (except maybe among a very small number of people). But to be fair that thesis makes for far less linkbaity a title than "What’s Wrong with OOP and FP".
Chill out people. The man is saying that the pure object-oriented model and the pure functional programming model have corners where they are lacking. He's not saying it's a bad idea to write object-oriented programs or pure functions.

If you have ever needed a little helper function in Java and wondered why you had to stick it in a static method in a class, I think you'll understand his point. Similarly, many of the Lisp programs I've seen end up with a fair amount of procedural code.

Basically, he's asking you to take a step back and appreciate what you have (and don't have), rather than what you think you have. This would lead to more interesting conversations.

If you have ever needed a little helper function in Java and wondered why you had to stick it in a static method in a class, I think you'll understand his point.

But that's a flaw in Java, not OOP, and it's not a consequence of Java being a pure OOP language (which it isn't).

If one wants to criticize OOP, the language to focus on should be Smalltalk.

He's also making accusations and not providing proof for them. As well as casting each paradigm in a negative light (extreme friendliness, extreme helpfulness, extreme eating all sound negative) without even defining what exactly "extreme OOP" or "extreme FP" are.
I can't speak for the author, but it seems he presented his argument poorly on OOP. I am not sure if it follows the same line of thought I have about OOP, but I regard OOP more as a mental model than a practical one. In that way I don't think everything is an object, more of there are nouns and there are verbs, and everything is to be described in such relationship. I prefer to think and work in component based approach though. Functional programming, on the other hand, is a set of rules over your mental model. Ultimately, I think, it boils down to your mental model which suits you best and, having said that, subjective reasoning. But that's how I think about it, doesn't have to be true.
I think the author also gets it wrong on the FP side, as he forgets the impure ones, like ML and Lispy ones, where side-effects are accepted.

This type of articles is also nonsense in a time and age where mainstream languages are going multi-paradigm and it is up to the developers to choose the best paradigms to model the application's architecture.

I think the author also gets it wrong on the FP side, as he forgets the impure ones, like ML and Lispy ones, where side-effects are accepted.

That is not in conflict with his argument. He does not argue that object-oriented programming or functional programming are wrong, just that taking a paradigm to an extreme (e.g. pure functional programming) is bad.

He is implicitly arguing for ML and Lisp and against Haskell, since e.g. ML is a functional language that recognizes that the world is mutable by allowing mutable data structures.

(Not that I agree - one could argue that Haskell acknowledges impurity even more by making it part of the type system.)

> Smalltalk functions are first-class objects

What about the functions inside those objects? And functions inside that function object. Like a chicken and egg problem. I neither agree nor disagree with him, what he's trying to say is that there is a difference between methods and functions. Methods (1) are functions (2) wrapped inside an object. (1) and (2) are different. He's just trying to say that "everything is an object" can't be rigid. At least that's what I understood.

It's been a long time since I had anything to do with Smalltalk, so I may be shitting you -- unfortunately, I'm also on the run and cannot properly check the following statement, so please take this with a grain of salt -- Smalltalk does not have named functions that aren't methods. In other words, if something takes arguments, returns things and has a name, it's always a method of an object (i.e. it's a message to which an object responds). In other words, while (in abstract terms), a method (1) is different from a function (2), Smalltalk only has (1). You can't define a "function" outside the scope of an object, like in C++. You could define a class that only has a long bunch of static methods, not modeling any kind of logical abstraction, but it's considered bad style.