by astigsen on 3/26/21, 10:43 AM with 83 comments
by dang on 3/26/21, 7:36 PM
by yellowapple on 3/26/21, 11:48 AM
You can see this in the objections:
> Objection 1 - Data structure and functions should not be bound together
...and yet the usual way to store data in Erlang is to make it an argument of a function repeatedly and recursively calling itself.
> Objection 2 - Everything has to be an object.
...which is the case in Erlang/OTP, when you consider that everything is a process a.k.a. actor a.k.a. object. And just like with Smalltalk, these objects communicate via messages.
> Objection 3 - In an OOPL data type definitions are spread out all over the place.
...as they are in Erlang, because (barring some bolted-on approaches like records) data type definitions exist entirely as patterns to be matched.
> Objection 4 - Objects have private state.
...as do Erlang's processes.
That is, while maybe Joe didn't pick up on it at the time (per comments on that article: https://news.ycombinator.com/item?id=26586829), it's clear that said article was more a complaint against OOP as popularly envisioned, with all its classes and inheritance and such - not against the notion of object orientation entirely, which Erlang happens to implement, even if it might've been by accident.
by jonathanaird on 3/26/21, 11:19 AM
OO has it’s place in the space of possible patterns to choose from based on the kind of solution you need. I think these overarching claims of superiority are missing the point completely. I personally like a combination of function and OO concepts that can work synergistically together and play the kind of role that they excel in individually.
by mtzet on 3/26/21, 11:29 AM
Should data and functions be together? That's a choice you can make on a case-by-case basis. It makes total sense that a HashMap has an insert() method. On the other hand, maybe your Player object is just some data which is interpreted by a PhysicsSystem and MovementSystem etc. Maybe it's clearer that all the physics calculations are in a common PhysicsSystem rather than being spread out into several implementing classes.
Should you use a switch-statement or a polymorphic interface? If you're traversing a graph with a fixed set of nodes, or you need to change all systems when new nodes arrives, the (exhaustive) switch may be preferable. I'd certainly prefer it to the visitor pattern.
If you can extract a calculation into a pure function, then that's great. If you're doing something inherently stateful, FP techniques are probably not too great. Maybe you can afford a high-level functional interface, but keep the implementation stateful.
The problem with OOP, FP, etc. is not that the techniqures are BAD, it's that all techniques are trade-offs. We're very good at talking about the advantages of some technique, but seem always forget the costs associated with it.
by meheleventyone on 3/26/21, 11:16 AM
There are a bunch of topics like this, in my own area you see the same four or five topics come up repeatedly with nothing new or interesting said on them.
I'm kind of intrigued if anyone has any thoughts on how to shock life back into these zombie debates?
by incrudible on 3/26/21, 11:12 AM
by wardb on 3/26/21, 11:23 AM
I can see that Erlang and Joe's (RIP) insights about OO might conflict with your view of the world, but IMHO that's just because of the OO indoctrination people grew up with.
Recommend learning key concepts of OTP/Erlang/Elixir first, and then revisit your initial opinions.
You might even conclude Joe Was Right
by mrweasel on 3/26/21, 11:20 AM
I would like to see someone do a language that enforces all of the OO paradigms and best practices on the programmer, just to see what the resulting code would look like and behave.
For instance, I'd love to see a compiler than throws a error if you violate the single responsibility principle. Much of the OO spaghetti code I've seen have been because objects are basically just structs with methods and 10 different ways and object could change state.
For practical purposes I doubt I'd ever use such a strict language, but it would be interesting to see if the result is safer and clear code. I suspect it's more code though.
by pron on 3/26/21, 11:53 AM
by js8 on 3/26/21, 11:37 AM
In some sense, OOP tends to conflate too many concepts into a single one, a class (or an object), to the point where it is harming the clarity of the code.
Ad objection #1: The post doesn't explain, why would you want to model the "natural clustering" of functions on pieces of data, why is not just adding an accidental complexity. Clusters of function and data types do tend to happen, so what? (In fact, they tend to happen differently for functions and data types, which makes encapsulation problematic.)
Ad objection #2: This has not been refuted.
Ad objection #3: I think this is pretty much a complaint about subtyping polymorphism, which yes tends to be a problem (AFAIK nobody really gave a good semantics to it). For example, a GUI container element needs to define a type of it's elements so it could define how to deal with them. It's difficult to use composition there, because you need to define certain functions on those elements, and many classic OOP languages (Java) do not let you add an interface to an existing class. Parametric polymorphism (and its enhancements like type classes) solves this problem much better, IMHO.
Ad objection #4: One of the problems of OOP is that modules also tend to be modeled with classes (see for example public/private access controls). But modules are a third distinct useful category of things, aside from functions and data types. OOP conflates these things into a single entity, an object (or a class).
I found in practice, much more useful is to have modules as a separate concept, and explicit import/export controls on the module boundaries. That is, do not tie access controls to functions or data types themselves. This makes modularization easier, because there is an additional layer (module definition) where you can override existing exports and imports.
by socialdemocrat on 3/26/21, 11:27 AM
Naturally if I criticize OOP it will be what is a currently used, not what might have been.
Also it is uncharitable. If you have experienced problems with OOP, then you know exactly what Armstrong is talking about.
And ironically many have said that Erlang is perhaps the most OOP oriented language if you follow the original idea of Alan Kay which was centered around message passing.
The OOP that dominates today originates with Simula and is part of another OO tradition.
by submeta on 3/26/21, 12:39 PM
Now thirty years later I know how to structure my (Python) code without OO: Via modules. I hardly use OO anymore. And I am drawn to FP more and more. But I see cases where OO is useful.
I don‘t think it‘s either/or. We should be glad we have all sorts of paradigms to go back to.
by firasd on 3/26/21, 11:19 AM
I'm surprised that the original anti-Object Oriented Programming article says "data structures and functions should not be bound together" since in functional programming (if we take that as the extreme opposite of OO) they are still kinda bound together. Like if you have a JS array ['cricket', 'football', 'badminton'] it is both data and almost like a function at the same time, since you can iterate through the items and make code execute for each
For example in a webpage if there is a div id=football then you can select it using:
['cricket', 'football', 'badminton'].forEach(function (i) { document.getElementById(i); } );
In my mind this type of code really blurs the line between variables and functions
by thedanbob on 3/26/21, 11:16 AM
by airhead969 on 3/26/21, 1:45 PM
On one side there is loose coupling between types and functions, and the other side has strong coupling. On one side, types can be composed simply and the other involves inheritance, overloading, and polymorphic rules.
Go, Haskell, and Idris are arguable examples of good type systems whereas languages like Python and Erlang have weak type systems. I wouldn't wish C++ on my worst enemy, but that's a religious preference not shared by all. Pascal without OO had a nice type system.
by xupybd on 3/26/21, 11:30 AM
Mixing data structures and models is something I find difficult. It can be hard to follow the logic of a simple function if it draws on several functions hidden inside an object. They same can be true of function composition, but many functional approaches allow you to pipe data through a series of transforms. This is hard to do when the objects have logic hidden within them.
by eric4smith on 3/26/21, 11:44 AM
by Kototama on 3/26/21, 12:39 PM
In top of that, a much bigger problem is that encapsulation is fundamentally broken, it does not prevent against what a real source of bugs: concurrent accesses on the object.
by enriquto on 3/26/21, 11:57 AM
The promoters of object-oriented design sometimes sound like master woodworkers waiting for the beauty of the physical block of wood to reveal itself before they begin to work. “Oh, look; if I turn the wood this way, the grain flows along the angle of the seat at just the right angle, see?” Great, nice chair. But will you notice the grain when you’re sitting on it? And what about next time? Sometimes the thing that needs to be made is not hiding in any block of wood.
by drkrab on 3/26/21, 12:27 PM
by ruph123 on 3/26/21, 5:01 PM
It is not technically OO but close enough. For me as someone who learned programming with Java (and some SML) and mostly worked with OOPLs after, I never had problems whatsoever. You don’t have inheritance but traits (and importantly trait objects).
The ways to model in Rust with Enums, Structs and Traits are amazing and somehow really fit my mental model.
by pansa2 on 3/26/21, 11:39 AM
For example, binary operator overloading. With unrelated classes `C` and `D`, should the implementation of `+` to handle `C.new() + D.new()` be added to `C` or `D`? Conversely, if both `C` and `D` define an implementation of `+`, which one should be preferred?
by indymike on 3/26/21, 12:48 PM
by teekert on 3/26/21, 12:49 PM
As a rookie it can be a tad jarring to read that OOP is an invention from the devil just to lure us into some hell later on. Now I know I can safely take such arguments with a grain of salt.
by iomodo on 3/26/21, 12:35 PM
by tmilard on 3/26/21, 12:28 PM
I tend to believe OO langage simply gives us less work to design & program solutions.
by zelphirkalt on 3/26/21, 1:15 PM
So it seems wisdom is slowly sinking in and we are progressing away from bundling state and behavior together.
Why is this done? For example to avoid having to subclass loads of stuff. For example in Rust one uses composition over inheritance and one can add behavior later by implementing traits, that make use of the parts of the structs. This way one does not need to subclass everything to add a new behavior. Similar for defmethod in Clojure. With Erlang this is raised to a whole new level, by defining the behavior in separate actors, which process messages, which are the data, which travel around without the behavior part. Overall it makes it possible to actually have encapsulation. A similar thing happens when we leave the boundaries of one machine and look at the web and REST, where we basically pass messages, conceptually similar to what happens between actors. For details about how encapsulation is broken in mainstream OOP read the article "Goodbye, Object Oriented Programming" by Charles Scalfani: https://medium.com/@cscalfani/goodbye-object-oriented-progra... (Sorry for the medium link. Does anyone know a better link?)
Next I'll address some things in the article, which I see problematic and questionable.
> Putting them together creates a boundary around this group of data and functions - encapsulation. Sure, you may call that a “module” but an object is IMHO slightly different since objects have identity, a life cycle and we can hold them, send them around etc.
That is not, what a module usually is. A module usually groups things, which are independent from each other, but are used in the same context. For example a module could contain a definition of a math functions. It does not track state or data. It might contain data structure _definitions_, but not their instances. A module is not a class or object replacement.
> So in some sense objects can probably be viewed as modules but often more fine granular. And we can combine such “modules” into larger modules (since objects have identity and can be referenced etc) and we can create and kill them dynamically (life cycle).
See above for the differences of objects and modules. It is not about being "more fine granular". It is different in character and should be used in different scenarios.
by pdimitar on 3/26/21, 1:26 PM