from Hacker News

Joe Is Wrong (2009)

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

    Imagine being a Smalltalker and still missing the underlying context of Joe's article - specifically, around the actor model (as used extensively in Erlang) being substantially closer to OOP as intended by e.g. Smalltalk than to the popularized inheritance-heavy brand of "OOP".

    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

    I think the conversation around OO suffers from the same problem that any major engineering trend suffers from: namely that eventually, the concept gets conflated with the way that enterprise software and overpaid consultants completely fudge the implementation of the concepts. Consultants get paid big bucks trying to convince your company that you need to be doing microservices or OO or nosql or whatever the latest fad is and that you need them to help you implement it. It’s not based on any real technical need. It’s just institutional FOMO.

    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

    These discussions tend to boil down to OOP vs FP. That's a false dichotomy. The alternative to OOP is not FP, it's pre-OOP imperative programming with various techniques a la carte.

    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

    The thing I find interesting about revisiting these old pieces is that the debate about the merits of OOP is effectively stymied. This post and the article it replied to posted yesterday (https://news.ycombinator.com/item?id=26586829) could effectively have been written today. And the debate in the comments tends to be pretty formulaic as well. It's one of those perennial topics that generates a lot of heat from rehashing tired arguments without generating much light in terms of new thoughts.

    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

    What I've found with all these "paradigms" is that they're only beneficial some of the time, usually not most of the time, never all of the time. It takes some experience and and an anti-dogmatic attitude to judge, for example, when a black box is a good choice and when plain old data is a good choice. Woe to those who attempt to shoehorn their idea of purity unto every application domain they encounter.
  • by wardb on 3/26/21, 11:23 AM

    Joe's original rant can be found here: http://harmful.cat-v.org/software/OO_programming/why_oo_suck...

    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

    Much of the debate or criticism of OO have been targetted at the theoretical side of object orientation, and perhaps less towards the practical implementations, which allows for some flexibility in the programmers approach to objects.

    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

    There's a fundamental problem with this kind of debates. Because we don't have a lot of empirical data and because human interaction is complex and often unintuitive, we don't have a good theoretical model of how the development and maintenance process actually works, the arguments tend to be based, in the best case, on unrepresentative and biased samples, and in the worst case, on personal aesthetic preference. And when we look at empirical data in the field, obviously whichever the dominant paradigm is, it will show the actual effect of programmers and shops at wildly different skill levels using it, while the non-dominant paradigm won't be able to show success at doing things better because, well, it doesn't have enough people at different skill levels using it.
  • by js8 on 3/26/21, 11:37 AM

    Here's my rebuttal of this rebuttal. For the record, I think OOP is wrong in the sense it had some advantage over the procedural programming, but that has since been superseded by functional (and type/category theory) approaches.

    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

    A problem with this kind of debate is that e.g. in their case the author is using quite a good OOP language which is hardly used by anyone.

    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

    When I started programming in Pascal in the early 90s, my procedures/functions and my data were decoupled, spread all over the place. Then I started learning (Turbo)Pascal with OOP. It was godsend. All of a sudden I could structure my code in a way I wasn’t able to do before.

    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

    Here's a Jan 2008 archive link for the article being responded to: https://web.archive.org/web/20080112111747/http://www.sics.s...

    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

    In my experience, when you have a field with two popular but different options and a lot of strong opinions about which is good and which is crap, neither side is right. Both sides have merit, otherwise they wouldn’t have a following. Examples: functional vs object-oriented programming, Ruby vs Python, Mac vs Windows, AC vs DC, rock vs pointy stick.
  • by airhead969 on 3/26/21, 1:45 PM

    To OO or not OO seems like more of a religious preference about functional containment: either at the module level or at the type level. This is why neither seems to satisfy everyone and why there are flame wars about it.

    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

    I find OO can wind up in a complex mess. If I have to look into 4 levels of inheritance I'm very lost. Encapsulation is great if your encapsulated black boxes function as they should. However that is often not the case, or at least you need to dig into that black box to understand the system.

    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

    As an aside R.I.P. Joe Armstrong.
  • by Kototama on 3/26/21, 12:39 PM

    One problem with encapsulation is that it is used to protect modification of the internal state of an object against external modifications but with very little benefits. Languages which are more relaxed on this subject (Python in some extend, for example) have shown that programmers are, most of the time, just not stupid enough to mess with the internals of an objects and that all the ceremony of encapsulation brings little value.

    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

    Good quote by Pike (2004):

    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

    Further context: Joe Armstrong and Alan Kay interview. https://youtu.be/fhOHn9TClXY
  • by ruph123 on 3/26/21, 5:01 PM

    I don’t want to toot this horn too much (is that a saying?) but I really like the path Rust took.

    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

    One issue I have with (pure) OOP is the way that methods are associated with a single type (and its subtypes). What about operations that don’t naturally belong to any one type?

    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

    OOP, FP or procedural, or even good old fashioned, start the top, run to the bottom imperative scripting all suffer from human beings choosing the wrong paradigm, or implementing that paradigm poorly. OOP has been largely a force for good, and the modern FP movement has been great, too. Same goes for dynamic typing and strong, static typing. It's all good if applied to the right problem.
  • by teekert on 3/26/21, 12:49 PM

    I only like this and the opposite post because now I know that even experts don't agree and I can feel good about using OOP when I feel like it is appropriate.

    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

    It’s all about your thinking process - the way you want to think when you solve a problem. It’s not about the programming language. You could use OOP even in C. And clearly there is no right or wrong here. As always, it depends, in some cases your problem is solved easily with OOP or FP or with imperative paradigm or any other.
  • by tmilard on 3/26/21, 12:28 PM

    Yes I oslo agree : For me, the the OO programming gets me close to the design of the problem I try to solve. I mean before even programming. Then when I feel ready to program my solution, Actors become classes naturally.

    I tend to believe OO langage simply gives us less work to design & program solutions.

  • by zelphirkalt on 3/26/21, 1:15 PM

    After many years of a lot of OOP, many people have realized, that putting behavior and state together into one entity might not be a good idea. We can see modern languages moving away from such practices. For example in Clojure one has defmethod, which defined a method separately. In Rust one has structs and implements traits for them separately. In some languages OO systems are libraries, which are optional, opt-in, not part of the core language. Think GOOPS and CLOS. Usually those languages define functions for data structures separately and you will have something like vector-length.

    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

    I gave up after his comments on the first OO objection. This blatant ad hominem and sweeping generalizations pass for good criticism? Not in my book at least.