Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

As a professional dev who has made a career out of working in oop languages and codebases, it took me far too long to realize that when it comes to oop, the emperor has no clothes.

To this day, oop advocates can't even agree on what oop even is or means.

Apparently oop as envisioned by Alan Kay was supposed to work like cells in the body that pass messages between each other and take actions independently.

Why? Who knows! It was never really explained why literally one of the most complex systems imaginable, one that we still really have very little idea how it even works, should be the model for what could and should probably be a lot simpler.

Today's modern oop languages are probably very far from what Kay envisioned (whatever that was), but it's remains unclear why classes and objects are "better" than the alternatives.

And before anyone goes and comments aksully code organization blabla like yes but code organization can be great or shit in oop or fp or procedural codebases, it has nothing to do with the "paradigm".

Let alone that the entrenched, canonical, idiomatic coding styles of most modern oop languages encourage state, mutability, nulls, exceptions and god knows how many trivially preventable entire classes of errors. Granted, most have now started to come around and are adopting more fp features and ideas every year, but still.

Don't get me wrong, writing programs like cells in the body that pass messages between each other and take actions independently is an interesting idea which deserves pursuing, if nothing else but to satisfy our curiosity and seeing to what if anything it's applicable and suited. (and even if the answer turns out to be "nothing", we've still learned something!)

But going from there to making strong claims about it being a more or less universally superior paradigm for computing and writing code, with little to zero evidence, that's a huge, huge stretch.

To the degree Erlang and Actors work, I think that's kind of a happy coincidence, and not due to any rigorous work on Alan Kay's part.



>Apparently oop as envisioned by Alan Kay was supposed to work like cells in the body that pass messages between each other and take actions independently.

IIRC he was interested in an abstraction that worked "all the way up" - he wanted the abstraction to essentially be a tiny computer. It's a cool idea with a lot of power. But it really is too powerful for applications, and leads to the same complexity problems you get in large distributed systems. And to not have 'function' as a primitive is inexcusable in any language.

Java's lack of a function primitive is a key weakness. It means you must have at least one "utility class" per project - a public class with static methods that are themselves simple functions. (The relatively recent introduction of lambdas, or anonymous inner classes, does not really address the problem. That's just ugly syntax sugar).


Is the fact that static methods in Java need to be namespaced to a class really anything to even worry about? Like, whats even a single issue with it?


The problem with Java's everything-must-be-a-class OO fundamentalism is that a computer is actually has data AND executable code. Java forces everything into a noun (class/data), but some things are actually verbs. You can these because they have awkward names, like Runnable.

See Steve Yegge's rant: https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo...


Oh sure. Java should have had function pointers sooner. The functional interfaces are just ok. C#'s actions and delegates are a bit better, imo.

But the parent went so far as to say that scoping functions to a class name is bad and I just don't see it. I think a pure global scope would make Java worse.


You've never seen Java (or C#) code where a bunch of unrelated functions belong to some meaningless class like "Utility"?

I do think both languages would benefit from allowing/ encouraging namespace-level functions (C# comes close with static classes but that's pretty clearly an abuse of any logical definition of the word "class"). However I probably wouldn't use them for implementing "controllers" in web applications - while I tend to agree it's not a good use of OOP, having key information about the current request/response available via properties of the current controller (and hence kept separate from function-level parameters that represent semantics specific to the endpoint being handled) plus various built-in functionality at the framework-supplied "controller" level isn't something I'd see a benefit in giving up.

But I'll admit there are few cases where a single controller class with methods for various CRUD like operations on a particular resource type makes any sense from an OOP perspective. I did think for C# what might actually work is to be able to define extension methods on the framework Controller class and have an automatic routing mechanism based on convention (e.g. if you defined an extension function "Get_Widget" then GET requests to /widgets would be automatically routed to it. Or it could be based on the namespace). But in general it would likely make the code more verbose (lack of implicit "this") for questionable gain.


> You've never seen Java (or C#) code where a bunch of unrelated functions belong to some meaningless class like "Utility"?

Is that worse than the global scope as the dumping ground?

One other benefit of forcing classes is that there's a logical place for private functions and fields. Private state is an anti-pattern to some but it's more consistent design for Java, I think.


> Is that worse than the global scope as the dumping ground?

I wouldn't have a problem with requiring everything to at least be in a namespace, though it needn't be enforced at the language level (vs a linter rule).


> You've never seen Java (or C#) code where a bunch of unrelated functions belong to some meaningless class like "Utility"?

I guess I don't see the problem here; what's wrong with this idea?

I've seen people claim "It's not OOP" but without any ability to explain why and I'd love to see the practical difference.


I don't have any problem with it not being OOP, but I do have a problem with the abuse of the concept of a "class". I also think it's rarely the ideal way to organise your code (even if the .NET core framework has examples of it - WebUtility/HttpUtility etc., and the JRE may well do too, though I can't see any obvious examples in the java.util namespace).


The entire java.util namespace is the example here.

Using static methods on a class seems perfectly fine to me; that's what they're there for. If you have a handy utility class to work around problems in the standard library, then that's an issue with the standard library; Java has this but has gotten better over the years.


I don't know about Java, but IMOE C# delegates often end up in callback hell. The events properties is just pure gasoline on the fire.


Might be heresy to come in to an OOP-bashing thread and say this, but the noun thing fits my mental model perfectly. IMHO, verbs aren't 'done' by nothing and data rarely exists without belonging to something, so it feels fine to me. I always find these discussions interesting though because clearly a lot of people feel differently (and very strongly in some cases).

(Please don't take this comment as an endorsement of OOP as practiced in the corporate world or that traumatic memory you have of an OOP codebase.)


Inform7 Actions and Rules system made me reconsider the centrality of the notion of Object when it comes to dispatching (note: Inform7 reads like english). Inform7 takes a verb-centric stance, then consider the subject (i.e. "this", in OOP), then the object (i.e. the arguments to a method). You can inject more specific behaviors in an action (i.e. something akin to a multimethod in CLOS), by adding an adjective to a verb/method complement/arguments. You can even use adjectives on the verb/sentence. All of this is gathered within a rulebook associated with the verb that exists aside of the actual source code: you can write code linearly, following the natural order of a given user story from the domain you model, using multiple verbs in succession, but can still display all a verb's rule, encompassing many user stories. Most importantly, it is truely extensible because you can't add code to an action's rulebook without doing so through a rule, which has the same role as a junction-point in aspect oriented programming or even subject-oriented-programming.

https://en.wikipedia.org/wiki/Subject-oriented_programming

https://en.wikipedia.org/wiki/Aspect-oriented_programming

https://ganelson.github.io/inform-website/book/WI_12_1.html

https://ganelson.github.io/inform-website/book/WI_19_1.html


You can use verbs passively to do something on something else. Or you can have your program or process do it. Logically, I see no difference between:

"The HandlerCreationFactory created a Handler based on the Request." (oop)

"A Handler is created based on the Request." (passive) or "The program (or process) creates a Handler based on the Request. "

Only that the first one needs a lot more nouns, even when they look silly.


Having a class whose only purpose is to have a run method feels like writing () => 2


Do you put all of your source code files in 1 folder?


I am confused on the relevance of this question


My obtuse point is that though sorting your source code files into various directories offers no functional benefit*, some folk find it useful from an organisational perspective. Maybe there's an element of that in housing functions an object.

*bear with me - admittedly there are languages/technologies where directory structures are required, but hopefully you get my point.


My point was that if a function in your language is

    class {public function call() {...}}
then it is going to be unergonimic to write what other languages write as

   array.map((elem)=>{...})


You didn't really address the question. Because a static method on a static class is a verb, even in the kingdom of nouns. It's just namespaced. So what's the issue? From a practical point of view.


"In fact there's a JVM language called The Nice programming language…"

https://nice.sourceforge.net/


I'd call it OO purism, close cousin though.


Personally issue with it (besides the pointless noise of having to make a private constructor and such) is that you can't alias them like you can with actual namespaces/modules in most languages, which becomes a pain when eg. you need functions from both Apache's StringUtils and your own StringUtils in the same file.


Ah, I suppose it can get wordy in Java. I'm more used to C# where you can alias the class name if need be.


Theres an argument that it teaches new programmers poor habits.


What poor habit and what argument?


Generally it goes with unnecessary boilerplate that gets copied around without any thought to whether it's necessary.

People who have been harmed by too much oop won't just define a function and call it. They'll define a class solely to contain that function and then call it more verbosely.

On the face of it it's not that bad but once you start tolerating a habit for doing things because that's how they're done and not because you actually think it's the right way you end up picking up all of these fragments from places where they do belong and forcing them into places where they don't.

Or at least, that's what I struggled with when I was new and insecure and wanted people to look at my code and think that I knew what I was doing because I showed evidence of knowing the dogmatic patterns, and I've seen it in others too.


Classes are basically namespaces, and trivial code doesn't need them, but quickly you do.

A lot of the criticisms being leveled here at oop actually me question their programming experience. There are many valid criticisms, but not a lot levelled here.


If all you're doing is using classes as superfluous namespacing, we'll that's hardly a threat. It's when they start having superfluous constructors that you're on a dark path. I once knew a guy who had a when-in-doubt-make-it-a-singleton habit, it was maddening to write tests anywhere near him.

Obviously that's an amateur move (and being an amateur myself I didn't have the confidence to show him a better way). But then it's never the skilled practitioners that make something look bad anyway.


Unnecessary wrappers. Obviously anything longer than

    print('hello world') 
is a waste. If the language violates Exupery's law why shouldn't developers?


> And to not have 'function' as a primitive…

"A block is essentially an anonymous function."

    [ :x | 1 + x ] value: 2
    ⇒ 3
https://cuis-smalltalk.github.io/TheCuisBook/Block-syntax.ht...


> Why? Who knows! It was never really explained why literally one of the most complex systems imaginable, one that we still really have very little idea how it even works, should be the model for what could and should probably be a lot simpler.

From my understanding, it is that every object has a very limited set of functionalities it manages. And that a goal is for each object to know as little as possible of the outside world. And along with that, to not bind to specific objects/classes but to bind to specific messages (Including multiple dispatch)


That was poorly achieved with oop and better achieved with fp though. Oop couples functionality and data (not typescript, if you don't want)


If you think that OOP advocates can't decide on what OOP is, don't look too hard at the FP side of the world. :D

Same goes for evidence based ideas. Odds are stupid high that you cannot find a single large scale codebase that has succeeded using any "pure" technique. Heck, I'd take small scale codebase for a fun thing to look at, at this point. Make performance a requirement, and really get ready for tears.


Jonathan Rees had said: "Here is an a la carte menu of features or properties that are related to these terms; I have heard OO defined to be many different subsets of this list." The aspects he names: "encapsulation", "protection", "ad hoc polymorphism", "parametric polymorphism", "everything is an object", "all you can do is send a message", "specification inheritance = subtyping", "implementation inheritance/reuse", "sum-of-product-of-function pattern". http://www.paulgraham.com/reesoo.html


> Apparently oop as envisioned by Alan Kay was supposed to work like cells in the body that pass messages between each other and take actions independently.

> Why? Who knows! It was never really explained…

Perhaps "Design Principles Behind Smalltalk" ?

https://www.cs.virginia.edu/~evans/cs655/readings/smalltalk....


>Let alone that the entrenched, canonical, idiomatic coding styles of most modern oop languages encourage state, mutability, nulls, exceptions and god knows how many trivially preventable entire classes of errors. Granted, most have now started to come around and are adopting more fp features and ideas every year, but still.

State isn't a feature solely of OOP. It's very easy to see the FP craze as a similarly dogmatic insistence on pure everything as removing useful features instead of preventing errors.

If FP truly was the indisputable way of the future, Scala would be the CRUDspeak of choice by now.


Elixir has opt-in state that works really well! Everything immutable until you need state.


> Apparently oop as envisioned by Alan Kay was supposed to work like cells in the body that pass messages between each other and take actions independently.

When I think of programs I think of modules of code that act like machines on a conveyor belt or assembly line operating on and pushing data along from station to station. This is why Erlang, Go and Plan 9's thread(2) library are how I want to program.

> To the degree Erlang and Actors work, I think that's kind of a happy coincidence, and not due to any rigorous work on Alan Kay's part.

https://www.infoq.com/interviews/johnson-armstrong-oop/ (3. Is Erlang object oriented?) three points: message passing, isolation between objects, and polymorphism.

The industry seems to have ran away with #3 while ignoring #2 and forgetting completely about #1.


What you are describing is what a directed acyclic graph achieves. It works well when you need to do a series of transformations. It does not work well when you need branching, loops and state at a high level. The mistake people make is that they see it work very well for certain parts of their software, then they give in to silver bullet syndrome and try to make it work for everything.


Most oop was designed in the context of desktop gui apps, which are much more complex that web apps imo.

Is-a vs has-a is a lot more clear in context of widgets and windows. It still isn't perfect, most popular gii used inheritance whereas composition would probably be better.

Inheritance ended up as a core component of oop principles when ultimately it is a flawed approach compared to more flexible composition methods.

Liskov substitution was a massive success, classes were an improvement over structs, private vs public was generally a good idea even if the astro architects went nuts with it.


> oop languages encourage state, mutability, nulls, exceptions and god knows how many trivially preventable entire classes of errors. Granted, most have now started to come around and are adopting more fp features and ideas every year

Do functional languages actually discourage "state, mutability, nulls, exceptions"

I know why we might argue against these features, but implying that functional programming doesn't have those features seems a little weird (or equivalent e.g. call/cc can cause execution flow less predictable than exceptions)!


My limited experience with FP languages suggests they encourage very explicit state management. You remove a lot of the footguns from having side-effects all over the place at the cost of the (sometimes, depending on the tooling and your familiarity with it) it being more complex to introduce state or external side-effects when you want them.


Once I got the hang of Elixir/Erlang it was just such a weight off my shoulders, honestly. Programming without entire classes of bugs is so much nicer.


And yet despite all these criticisms and complaints, C++, Java, Python, Javascript, PHP and even Ruby to some extent, have been massively popular and used to write all sorts of software we rely on.


What people are complaining about when they voice these sorts of concerns goes back to the concept of essential vs accidental complexity proposed by Fred Brooks. The issue is not that adding accidental, inessential complexity to software is somehow a sin. The problem is that someone will have to wrangle this extra complexity. Enterprises can just throw bodies at the problem, creating drudgery. The warning is against producing pointless, boring work.


All of those languages also have functional/procedural language features. You don't have to go crazy with Classes/Objects to program in them.


Which is what’s great about these languages - best of both worlds.

I also think this discussion shouldn’t be about languages at all, you can have objects of some form in any imperative language including C. It’s more about what design patterns make sense for specific problems, and sometimes the answer is OO-style (eg with class hierarchies) and sometimes not.


(nobody is talking about whether X pattern can be used successfully)


I do some programming, but not OOP. Over the years I've read about it and asked colleagues about it, and the why question finally settled on it helps you organize your code. I can get behind that. But I also want to understand why OOP instead of conventional programming; I'm still waiting for that part to click. Until that question is answered it won't be capturing my full attention.


What is "conventional programming", pray tell?


Functional? Procedural? Logic? Dataflow? Relational? Unstructured imperative, like old-school BASIC?

I mean, the only clue we have is that it is some paradigm that isn’t OO, but that leaves... a whole lof options.


Maybe procedural?


I think the root rant here, which I personally find unconvincing, is thinking in terms of OOP vs functional programming. Procedural isn't the opposite of OO; objects are composed of many procedures.


I wasn't intending to trigger anyone but it seems I have. Perhaps I should have stated it as, "why OOP instead of organizing the code through other means?" It's a genuine question I have, and it just hasn't clicked for me yet, personally. So it's not really trying to be convincing at all; I'm just sharing my experience.


The most compelling scenario for OOP is situations where, in its absence, a developer would tend towards reimplementing some OOP concepts over time. I think it is a natural fit for game development and some other things, but I don't think it melds so well with that thing we call 'business logic'.


OOP works well when state and behavior are tightly coupled, particularly for things which are naturally modeled as state machines. Device drivers tend to be perfectly suited to OOP organization: the underlying device has state that needs to be tracked, it has behaviors which can be controlled (usually by sending particular messages), and there are often multiple implementations of the hardware which should all expose the same high-level API. OOP allows bundling the state handling with the control messages needed, exposing a single common API while allowing specialized implementations for particular driver implementations.

For the higher-level code, it's often less useful.


I don't think there is a satisfying case for OOP.

https://youtu.be/QM1iUe6IofM?t=477


Algol


I've never really seen this explicitly stated anywhere, so maybe this is some super idiosyncratic idea on my part, but it's always seemed to me that one of the key pieces of OO is the use of polymorphism as a sort of syntactic/conceptual sugar around function indirection, that many people seem to have an easier time understanding and reasoning about.


I like the mapping to Claude Shannon’s ideas in information theory.

https://cs.stanford.edu/people/eroberts/courses/soco/project...


> And before anyone goes and comments aksully code organization blabla

I'll bite. Having worked on "enterprise" application the OOP (or more like the dreaded enterprise patterns) have sort of "clicked" to me.

An aside. Git-flow is more than decade old at this point and people still have heated debates from time to time on whether that approach is any good. In essence git-flow merely introduces organization to patchsets. If you are building a SaaS like system, most probably every environment you have runs "current" code and each stage (prod, test, dev) is merely a shifted pointer that will eventually catch up. Why would you structure your teamwork around identifiable patchsets, why would you keep that information around when you do trunk-based development? There is seemingly no need to do that and you would rightfully diss on git-flow as overly complex.

However, if you build hardware appliances or installable offline application with multiple versions (think e.g. MS Office in 2000-2010 - multiple versions and tiers of those out in the wild under support), suddenly there are situations where you do in fact need to checkout and work on an exact patchset that is installed at customer site. If your field requires any form of certification, you will quickly learn that having patchsets provably not touching frozen (already certified or submitted for certification) code is highly valuable. Ability to cherry-pick a hotfix can save organization man-months.

Back to code organization. So you have this "enterprise" application that has various software modules/components, some of which are built by external contractors, some are off-the-shelf components, some have been left and forgotten five years ago and for one reason or another you want to work on one of these components. Whatever team you assemble, majority will have zero understanding of assumptions baked into code. Documentation, however meticulously maintained, will still leave holes in understanding. How do you make changes to a decade old component that is used in various weird ways all throughout the project with any amount of confidence that you are not breaking stuff at a distance? Apparently, OOP enterprise patterns that introduce decoupling points for implementation details and pass around objects with assumptions abstracted away are pretty solid barriers. They are cumbersome, difficult to use at early stages of development when all complexity of the task can fit into the heads of programmers implementing features. Similarly to git-flow, the advantages of these structures only become apparent when you start needing them and that will quite probably be when the principal engineers greenfielding the project have long left the company altogether.

A very specific example: factory pattern. Why would you have a layer of indirection to simply instantiate an object? All refcounting shenanigans aside, you just cannot change interface of object/component without affecting call sites. Call sites that may be outside of your control, code frozen or simply too dreadful to touch without strong justification. Factory pattern introduces a decoupling point between call sites and implementation. Having a factory you can implement interface shims and leave call sites untouched while having the freedom to rework component and its interface. You just cannot achieve that without OOP.

> Let alone that the entrenched, canonical, idiomatic coding styles of most modern oop languages encourage state, mutability, nulls, exceptions and god knows how many trivially preventable entire classes of errors.

I will agree that OOP implementations in mainstream languages leave much to be desired. That is part of the price we pay for backwards compatibility.


You can introduce indirection layers without OOP since a function's signature is its interface. I do it all the time.


I remember learning and using OOP in computer science classes and then going to apply it to a game I was working on. I object-ified everything! It felt like it all just made sense.

Then I ran the game. And it crawled at 15fps and I wondered why.

Of course I was new to programming so I can’t just blame OOP but it was my first lesson in how OOP isn’t a magic wand by any means. And later I got into more functional programming and today I find myself writing function-based components in react for a living and loving it. It’s funny how things go like that.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: