Rochus 2 days ago

> The industry and the academy have used the term “object-oriented” to mean so many different things.

I think we can safely stick to how IEEE defines OOP: the combination of three main features: 1) encapsulation of data and code 2) inheritance and late binding 3) dynamic object generation (from https://ethw.org/Milestones:Object-Oriented_Programming,_196...).

The article assumes that C++, Java, and Smalltalk implement completely different subsets of OOP features, which is not true at all. Those languages, including Smalltalk (starting with Smalltalk-76), all implement the Simula 67 object model with classes, inheritance and virtual method dispatch. Simula 67 was the first object-oriented programming language (even if the term was only applied ~10 years later in a 1976 MIT publication for the first time, see https://news.ycombinator.com/item?id=36879311). Message passing (the feature the article claims is unique to Smalltalk) is mathematically isomorphic to virtual method dispatch; and also Smalltalk uses method dispatch tables, very similar to C++ and Java.

  • jillesvangurp a day ago

    The problem with that definition is that modern languages like Rust, Go, and Javascript fall between the cracks here with de-emphasizing things like classes and inheritance while still providing things like generics, interfaces, encapsulation, etc.

    For example, Javascript was influenced by a few languages, one of which was a language called Self which is a Smalltalk like language where instead of instantiating classes you clone existing objects. It's a prototype based language. So, there weren't any classes for a long time in Javascript. Which is why there are these weird class like module conventions where you create an object with functions inside that you expose. Later versions of ECMA script add classes as syntactic sugar for that. And Typescript does the same. But it's all prototypes underneath.

    Go has this notion of structs that implement interfaces implicitly via duck typing. So you don't have to explicitly implement the interface but you can treat an object as if it implemented the interface simply if it full fills the contract provided by that interface. Are they objects? Maybe, maybe not. No object identity. Unless you consider a pointer/memory reference as an identifier.

    Rust has traits and other constructs. So it definitely has a notion of encapsulation, polymorphism, etc., which are things associated with OOP. But things like classes are not part of Rust and inheritance is not a thing. It also does not have object identity because it emphasizes things like immutability and ownership.

    Many modern languages have some notion of objects though. Many of the languages that have classes tend to discourage inheritance. E.g. Kotlin's classes are closed by default (you can't extend them). The narrow IEEE definition is at this point a bit dated and reflects the early thinking in the sixties and seventies on OOP. A lot has happened since then.

    I don't think a lot of these discussions are that productive because they conflate a lot of concepts and dumb things down too much. A lot of it boils down to "I heard that inheritance is bad because 'inject reasons' and therefore language X sucks". That's maybe a bit harsh on some languages that are very widely used at this point.

    • throwup238 a day ago

      > Rust has traits and other constructs. So it definitely has a notion of encapsulation, polymorphism, etc., which are things associated with OOP.

      Why do you associate polymorphism with OOP? It’s a pretty universal PL concept. Haskell’s polymorphism is one of the defining features of its type system.

    • Rochus a day ago

      Well, C++ also has higher-order functions and lambda expressions, and yet we don't call it a functional programming language.

      • pjmlp 9 hours ago

        Are you sure?

        Search for "Functional Programming in C++: How to improve your C++ programs using functional techniques".

        • Rochus 6 hours ago

          Yes. It's by standard a "general purpose" programming language, not a "functional" language, and it definitely corresponds with the IEEE definition for OOP (in contrast to the mentioned Rust or Go languages). I thought I would give a clear example that the fellow would immediately understand. However, I seem to have underestimated the postmodernist tendencies of today's world.

          • CyberDildonics 29 minutes ago

            If it's a general purpose language and not a functional language just because it has function features, then why would it be an 'OOP' language just because it has 'OOP' features?

  • LukeShu 2 days ago

    Inheritance is just the unnecessary coupling of composition and polymorphism.

    • saghm a day ago

      Even before I got to the point where I decided I didn't like inheritance, I distinctly recall having conversations about how I felt that using inheritance for anything other than polymorphism didn't usually end up with particularly clean code. I can remember a conversation about this at least as far back as the summer after my freshman year of college, and I don't think I was aware of the idea of "composition" yet, because I remember phrasing my point as something like "inheritance shouldn't be used for 'code-sharing', only for polymorphism".

      • lukan a day ago

        Out of curiosity, when you say you don't like inheritance, does that mean you never use it at all, or you only use it rarely?

        Because even though inheritance often is used in a wrong way, there are definitely cases, where it is the clearest pattern in my opinion.

        Like graphic libary things. E.g. everything on the screen is a DisplayObject. Simple Textfields and Images inherit directly from DisplayObject. Layoutcontainers inherit from DisplayObjectContainer which inherits from DisplayObject.

        Inheritance here makes a lot of sense to me and I don't see how it could be expressed in a different way without loosing that clarity.

        • ffsm8 a day ago

          > I don't see how it could be expressed in a different way without loosing that clarity.

          What value does the inheritance provide here?

          Can't you just use a flat interface per usecase without inheritance and it will work simpler with less mental overhead keeping the hierarchy in mind?

          Explicitly your graphic library sounds should be fine to have the interface DisplayObject which you can then add default implementations on. (That's a form of composition)

          • lukan a day ago

            That would be way, way more verbose for everything.

            Every display object has a x y width and height for example. And there is basic validating for every object. Now a validate method can be conposited. But variables? Also the validating, there is some base validating every object share (called wih super) and the specific validating (or rendering) is done down in the subclasses.

            And even for simple things, you can composite a extra object, but then you cannot do a.x = b.x * 2 anymore, but would have to do a.po.x = b.po.x * 2 etc

            • bccdee 21 hours ago

              > Every display object has a x y width and height for example.

              Intuitively¹, I feel like this is something that should be separated out into a BoundingBox object. Every component that needs a bounding box satisfies a small `HasBoundingBox { getBoundingBox(self) -> BoundingBox }` interface. Maybe there's a larger `Resizeable` interface which (given a type that satisfies `HasBoundingBox`) specifies an additional `setBoundingBox(self, bb)` method.

              You don't end up with a tidy hierarchy this way, but I'm not sure you'd end up with a tidy hierarchy using inheritance, either. I feel like this sort of UI work leads toward diamond inheritance, mixins, or decorators, all of which complicate inheritance hierarchy. Flat, compositional design pushes you toward smaller interfaces and more explicit implementations, and I like that. The verbosity can be kept in check with good design, and with bad design, the failure mode leans towards more verbosity instead of more complexity.

              For more complicated features, composition & interfaces can make things more verbose, but honestly I like that. Inheritance's most powerful feature is open recursion (defined in the linked article), and I find open recursion to be implicit and thorny. If you need that level of power, I'd rather the corresponding structure be explicit, with builders and closures and such.

              [1]: Not saying this is correct, but as someone who prefers composition to inheritance, this is what feels natural to me.

              • lukan 16 hours ago

                "You don't end up with a tidy hierarchy this way, but I'm not sure you'd end up with a tidy hierarchy using inheritance, either."

                Well, I am sure, that all the graphic libaries I ever used, had this inheritance model. (The graphics libary I build, as well.)

                The libaries I have seen, that used a different model, I did not really like and they were also rather exotic, than in wide use. But I am willing to take a look at better designed succesful inheritance free ones, to see how it can be done,.if you happen to know one ..

                • bccdee 15 hours ago

                  None of the graphics libraries you've ever used were built with mixins, decorators, or multiple inheritance? I confess I never went too deep with GUI toolkit programming, but Swing, for instance, definitely uses decorators (e.g. JLayer).

                  Neither Go nor Rust have inheritance, so any graphic library implemented in those languages will be inheritance-free; ditto anything in a functional language, for the most part. In general, these tend to be very declarative toolkits, being post-React, but they should illustrate the point. For something more widely used in industry, I know Imgui is a popular immediate-mode library.

                  • amypetrik8 13 hours ago

                    I mean if you writing joe slop tax softare you can use whatever mixin decorator singleton factory performance be damned, slop doesn't need performance, in fact, is frowned upon in slop developers as too showy and flashy, puts people off.

                    Now if you're say writing a high performance game, rendering engine, then maybe you want to squeeze out another 10 frames per second (FPS) but not committing resources to the overhead of that mixin decorator singleton factory facade messenger design pattern and just have some concrete tight C or assembly loop at the beating heart of it all

            • saghm a day ago

              To avoid splitting the discussion by responding directly to your comment above, since I have thoughts about this one as well: I've written Rust professionally since 2019, which doesn't have inheritance, so I don't use it at all. I guess my point is that I don't miss having inheritance as a tool in my everyday coding, and I actively prefer not having it available in Rust.

              In terms of what you're saying here, the extra verbosity is not really something that either bothers me or is impossible to work around in the context of Rust at least. The standard library in Rust has a trait called `Deref` that lets you automatically delegate method calls without needing to specify the target (which is more than sufficient unless you're trying to emulate multiple inheritance, and I consider not providing support for anything like that a feature rather than a shortcoming).

              If I were extremely bothered by the need do do `a.po.x` in the example you give, I'd be able to write code like this:

                  struct Point {
                      x: i32,
                      y: i32,
                  }
              
                  struct ThingWithPoint {
                      po: Point,
                  }
              
                  impl Deref for ThingWithPoint {
                      type Target = Point;
              
                      fn deref(&self) -> &Self::Target {
                          &self.po
                      }
                  }
              
                  fn something(a: &mut ThingWithPoint, b: ThingWithPoint) {
                      a.x = b.x * 2;
                  }
              
              Does implementing `Deref` require a bit more code than saying something like `ThingWithPoint: Point` as part of the type definition? Yes (although arguably that has as much to do with how Rust defines methods as part of `impl` blocks outside of the type definition, so defining a method that isn't part of a trait would still be a slightly more verbose, and it's not really something that I particularly have an issue with). Do I find that I'm unhappy with needing to be explicit about this sort of thing rather than having the language provide an extremely terse syntax for inheritance? Absolutely not; the extra syntax convenience is just that, a convenience, and in practice I find it's just as likely to make things more confusing if used too often than it is to make things easier to understand. More to the point, there's absolutely no reason that makes sense to me why the convenience of syntax needs to be coupled with a feature that actually changes the semantics of the type where I want that convenience; as comment I originally replied to stated, inheritance tries to address two very different concerns, and I feel pretty strongly that ends up being more trouble than it's worth compared to just having language features that address them separately.
    • o_nate 19 hours ago

      Inheritance is not necessary, but then very few programming constructs are absolutely necessary. The question is does it help program clarity or not. I think that in some cases, used sparingly, it can. The main danger of inheritance is not that it is OO, but that it is not OO enough. It breaks encapsulation by mixing properties and methods between base classes and derived classes without clear boundaries. Composition is safer because it preserves encapsulation. In general, I think that protected abstract methods are a code smell, because they usually indicate close coupling of details that should be kept separate between the base and derived classes. But used correctly, inheritance can be more succinct and convenient.

    • pjc50 a day ago

      Delegation is a very useful part of composition. Almost all OOP languages have two techniques to delegate some methods to another object:

      - manually write a bunch of forwarding methods and remember to keep them updated, or

      - inheritance.

      • bccdee 21 hours ago

        To be fair, the compiler generally forces you to keep the forwarding methods updated. It can be irritating, but there's little risk of forgetting in a statically-typed language.

        Manual forwarding also operates as a forcing function to write small interfaces and to keep different pieces of logic separated in different layers, both of which feel like good design to me. (Though I'm not saying I'd turn my nose up at a terser notation for method forwarding, haha.)

  • vips7L a day ago

    Object orientism is just encapsulation. It’s the only thing that is required. You can have objects without inheritance and virtual dispatch.

    • bccdee 21 hours ago

      Languages like Go and Rust have module-scoped visibility, though. This is definitely encapsulation, but I wouldn't call it inherently OO.

    • vbezhenar a day ago

      So Python is not OOP language? You can't hide fields.

      • nr378 a day ago

        You can hide fields in Python with a little bit of gymnastics:

          class EncapsulatedCounter:
              def __init__(self, initial_value):
                  _count = initial_value
        
                  def increment():
                      nonlocal _count
                      _count += 1
                      return _count
        
                  self.increment = increment
        
        
          counter = EncapsulatedCounter(100)
          new_value = counter.increment()
          print(f"New value is: {new_value}")
        • d0mine 19 hours ago

          Usually, a simple function is enough:

              def make_counter(start=0):
                count = start
                def incr():
                  nonlocal count
                  count += 1
                  return count
                return incr
          
          Example:

              >>> c = make_counter()
              >>> c()
              1
              >>> c()
              2
          
          But it hides nothing:

              >>> c.__closure__[0].cell_contents
              2
              >>> c.__closure__[0].cell_contents = -1
              >>> c()
              0
          
          "private" in Python is cultural, not enforced. (you can access `self.__private` from outside too if you want).
      • bluGill a day ago

        It has conventions to hide data. Good enough.

      • vips7L a day ago

        Python was a mistake if you ask me ¯\_(ツ)_/¯

        • cpburns2009 a day ago

          Python is the worst programming language except for all of the others.

    • taberiand a day ago

      I would say specifically encapsulation of mutable data

      • vips7L a day ago

        Lots of modern OO uses immutable data.

        • bccdee 21 hours ago

          A lot of the underlying intuition behind OOP is expressed as "cells exchanging messages like an organism" and such, and I think that implies the distribution of state among a variety of small, special-purpose state-managers. More functional variants of OOP where state is managed centrally are a meaningful shift away from traditional ideas of OOP, and I think calling them both equally OO glosses over that shift a little.

        • taberiand 18 hours ago

          What's a good example? What comes to my mind is modern C# which I would say is a multi-paradigm language that encourages things like immutable records and interfaces and composition over inheritance as alternatives to the now less favoured OOP styles that it also supports

  • cjfd a day ago

    (1) and (2) sound reasonable enough. What do they mean by "dynamic object generation"?

    • cess11 a day ago

      Runtime instantiation.

      From the link above:

      "Instead of seeing a program as a monolithic structure, the code of a SIMULA program was organized in a number of classes and blocks. Classes could be dynamically instantiated at run-time, and such an instance was called an "object". An object was an autonomous entity, with its own data and computational capabilities organized in procedures (methods), and objects could cooperate by asking another object to perform a procedure (i.e., by a remote call to a procedure of another object)."

austin-cheney a day ago

> OOP-bashing seems fashionable nowadays.

Yes, and for just cause. OOP was invented in Simula76 (1976) and popularized in C++ (1982). OOP solved a very real problem of allowing applications to logically scale in memory constrained systems by allowing logic to grow independently and yet retain access to memory already claimed by a parent structure. Amazing.

Now fast forward and you get languages like Java, Go, and JavaScript. These languages are garbage collected. Developers have absolutely no control over memory management in those languages. None at all. It really doesn't matter because the underlying runtime engines that power these languages are going to do whatever is in the best interest of execution speed completely irrespective of application or memory size. We just don't live in a world where the benefits offered by OOP exist. Technology has moved on.

The only reason for OOP today is code culture. Its a form of organizational vanity that continues to be taught because its what people from prior generations were taught. Most new languages from the last 15 years have moved away from OOP because it contains a lot of overhead as decoration with no further utility.

  • fmjrey 20 hours ago

    OOP certainly has some early roots in trying to be more efficient with code reuse, organization, and clarity of intent. Later on Java tried to alleviate serious productivity and security issues with garbage collection and cross platform portability. It certainly increased the distance between the hardware and the developer because there are more levels of indirection that can now degrade performance.

    However with hardware progress, performance is not the only critical criteria when systems grow in size, in variety of hardware, with internet volumes, in the number of moving parts, and of people working on them. Equally if not more important are: maintainability, expressivity so less lines of code are written, and overall the ability to focus on essential complexity rather than the accidental one introduced by the langage, framework, and platform. In the world of enterprise software Java was welcomed with so much cheers that indeed a "code culture" started that grew to an unprecedented scale, internet scale really, on which OO rode as well.

    However not all control is lost as you say. The JVM that also runs more advanced langages with a JIT that alleviates some of the loss of performance due to the levels of indirections. GC are increasingly effective and tunable. Also off-heap data structures such as ring buffers exist to achieve performance comparable to C when needed. See Martin Thompson video talks on mechanical sympathy, which he gave after working on high frequency trading on the JVM, and check his later work on Aeron (https://aeron.io/). As usual it's all about trade-offs.

  • franktankbank a day ago

    I don't follow. Java is mega object oriented. Can you even write a standalone function? I get the impression you are focusing on a single aspect of OOP that has gone out of fashion without realizing by and large every application developer is doing OO development 99% of the time.

    • austin-cheney a day ago

      Nonsense. JavaScript developers will also tell you everything is framework. What these things really mean is that the person making the statement has only 1 perspective and so everything they see matches the only single lens through which they view the world.

      All definitions of OOP in common use include some form of inheritance. That said I do OOP 0% of the time in my programming. Most developers I have worked with never do OOP unless the given language or employer forces it.

rawgabbit 2 days ago

Muratori traced the history of OOP to the original documents. Skip to the 1:18 mark if you want to skip to his findings.

https://youtu.be/wo84LFzx5nI

  • infogulch a day ago

    The craziest thing in that video is realizing that the Entity Component architecture was actually invented for Sketchpad in 1963, but the whole idea was slept on until Looking Glass reinvented it in 1998 for Thief: The Dark Project.

  • asabla a day ago

    This is such a good video. I really like the way he presents it as well.

    His rant about CS historians is also a fun subject

noelwelsh a day ago

The trick is to say "codata" instead of "object-oriented programming", and then you can use OOP and still be a programming hipster. (I am a programming hipster.)

I'm only somewhat joking. I actually find this view very useful. Codata is basically programming to interfaces, which we can think of as OO without confusing implementation approaches like inheritance. Codata is the dual to (algebraic) data, meaning we can convert one to the other. We can think of working with an abstract API, which we realise as codata or data depending on what best suits the project. More in the book I'm writing [1].

In general I agree with the author. There are a lot of concepts tangled up in OOP and discussion around the benefits of OOP are rarely productive.

[1]: https://scalawithcats.com/

dmux 2 days ago

Regarding Message Passing and Late-binding, I think it's important to take into account that Alan Kay was working on Smalltalk -- a system that was image based; a system where you could change things as it was running. I think that message passing and late-binding are often championed but then sort of fall flat given standard deployment techniques: build & deploy (often to a ephemeral runtime / container).

  • igouy 2 days ago

    Smalltalk can use build & deploy. (Image as cache, not archive.)

    "At the outset of a project involving two or more programmers: Do assign a member of the team to be the version manager. … The responsibilities of the version manager consist of collecting and cataloging code files submitted by all members of the team, periodically building a new system image incorporating all submitted code files, and releasing the image for use by the team. The version manager stores the current release and all code files for that release in a central place, allowing team members read access, and disallowing write access for anyone except the version manager."

    1984 "Smalltalk-80 The Interactive Programming Environment" page 500

    • dmux 2 days ago

      Yes, you could also build and deploy a Smalltalk system, but my point is that the “build & deploy” approach (to me) seems antithetical to the message passing and late-binding paradigms. To use another example, it seems like you lose a lot of the benefits of Common Lisp via Slime (hot code reloading) if you deploy your Common Lisp app to a short-lived, ephemeral environment.

      • igouy a day ago

        > (to me) seems antithetical to the message passing and late-binding paradigms

        (To me) seems like build & deploy as dev process and message-passing & late-binding as implementation technique.

        Separate concerns, I probably misunderstood.

travisgriggs 2 days ago

For me, the fundamental gestalt was/is binding behavior to data. I found/find it useful for modeling how things work. I was always inspired by one of Alan’s seed inspiration for Smalltalk, how biological cells accomplish computation through interacting with each other. I did Smalltalk for many years before polyglotting.

ChrisMarshallNY 2 days ago

I always considered an "object" to be data with identity and state.

All the other stuff, like polymorphism, encapsulation, etc., I consider "addons."

  • vjvjvjvjghv 2 days ago

    I think the biggest mistake was to teach inheritance as a main feature of OOP. I have done some stuff with inheritance but it was very specialized and it would have been fine without inheritance.

    • ChrisMarshallNY 2 days ago

      Back in the day, I used to do OOP with C.

      It was a common pattern, back then. We’d pass around structs, and have a small library of functions that accessed/modified the data in these structs.

      If you wanted, you could add function pointers to the structs. You could add “polymorphism,” by overwriting these pointers, but it was messy.

      That said, inheritance can be very useful, in some cases, like improving DRY. I don’t like to take absolute stances, so much, these days.

      • 21asdffdsa12 a day ago

        Reminds me of cube the engine of sauberbraten2 - who uses a C/C++ hybrid with complex inheritance to inherit methods - like everything is a starts as an object. And so on and so forth.. turning inheritance into basically a mapped out set theory with the final class being the outermost part of a set holding the abilities of all inner sets. https://github.com/embeddedc/sauerbraten/tree/master/src

        Its not very intuitive used that way.

    • mrkeen a day ago

      But OOP needs something to distinguish it from the rest, otherwise it's just P.

      Do people honestly think other languages don't do whatever definition OOP has today? Encapsulation & polymorphism? Message-passing & late-binding?

      Inheritance is the one thing that the other languages took a look at and said 'nope' to.

      (Also, the OOP texts say to prefer composition anyway)

      • nrds a day ago

        IME with this sort of thread there is a huge correlation between praising OOP and believing that encapsulation is an identifying feature of OOP. Also polymorphism to a much lesser extent, but the other two almost none.

        It is very difficult to tell whether this is a definitional problem - people believe any kind of encapsulation is OOP - or if some people can't wrap their heads around how to do encapsulation without message passing and the rest.

zyxzevn a day ago

I used "Open Recursion" in many large (ObjectPascal / C++) projects. With simple interfaces, a large project becomes a collection of smaller components. I noticed many programmers do not understand it. Pure OOP languages (like Smalltalk or Ruby or Scala) are the best languages to understand how it could work. They usually have closures where other languages would have "patterns".

The problem is that the components are often connected to different interfaces/graphs. Components can never be fully separated due to debug, visualization and storage requirements.

In non-OOP systems the interfaces are closed or absent, so you get huge debug, visualization and storage functions that do everything. On addition to the other functionality. And these functions need to be updated for each different type of data. The complexity moves to a different part. But most importantly, any new type requires changes to many functions. This affects a team and well tested code. If your product is used by different companies with different requirements (different data types), your functions become overly complex.

hgs3 a day ago

> I feel that prototypes are harder to wrap one’s head around compared to classes.

This is sad to read because prototypes are conceptually easier to understand than classes. It’s unfortunate that most developers experience with them is JavaScript, because its implementation is extremely poor. I recommend trying Io which is very Self inspired as well as Lua.

jolt42 2 days ago

My OO projects were usually in Java with a DB. They all ran afoul of what Martin Fowler calls the Anemic Domain Model. Basically your objects are data-only, so there's no benefit. In addition Spring injection became ubiquitous, and further killed objects with behavior. The only project using a DB and had objects with behavior was an old one that happened to use TopLink as an OR mapping.

  • ejflick 2 days ago

    > Basically your objects are data-only, so there's no benefit.

    This makes me wonder why most of us use Java at all. In your typical web app project, classes just feel like either:

    1) Data structures. This I suspect is a result of ORM's not really being ORM's but actually "Structural Relational Mappers".

    - or -

    2) Namespaces to dump functions. These are your run-of-the-mill "utils" classes or "service" classes, etc.

    The more I work in Java, the more I feel friction between the language, its identity(OO beginning to incorporate functional ideas), and how people write in it.

    • marcosdumay 2 days ago

      > why most of us use Java at all

      Java was the first popular language to push static analysis for correctness. It was the "if it compiles, it runs" language of its day, what meant that managers could hire a couple of bad developers by mistake and it wouldn't destroy the entire team's productivity.

      I'm not sure that position lasted for even 5 years. But it had a very unique and relevant value proposition at the time.

      • javcasas a day ago

        OCaml would like to have a word with you. In 2005 it already had better static analysis and correctness on object oriented stuff than what Java struggles to approach today.

        But Java has better marketing.

        • marcosdumay a day ago

          Java is from 1996...

          It got really useful by 1998.

          • javcasas a day ago

            OCaml is also from 1996. And I say 2005 because that's when I started using it, not when it started being useful.

            By that time it supported parametric generics, multiple inheritance, named parameters, optionals instead of nulls everywhere, compile to machine code and quite a few extra things that I couldn't understand at the time.

    • elric a day ago

      A lot of that is down to how people rely on frameworks that force them into "convenient" abstractions. Like Spring and Hibernate. But those are not the language. They represent a (vocal) subset of programmers.

      You don't need an ORM or an overgrown dependency injection framework to create a webapp in Java.

    • vips7L a day ago

      Service classes are the thing I hate most. They’re just namespaces for functions. They’re a product of Java not being able to have top level functions.

      • morshu9001 a day ago

        They're just namespaces for functions, and... why is that so bad? Of all the reasons I hate Java, this isn't one of them, it's whatever.

      • elric a day ago

        Not being able to have top level functions is a feature, not a bug.

        You can declare static methods on interfaces in Java, which means you could call things like Users.create("Foobar") if you wanted to.

        • javcasas a day ago

          Not everything can be associated to a single entity. Many operations work on two or more entities where none of them are the "master". Otherwise you end up with "coordinators".

          Users.create(...) is the easy case. Try transfer_permissions(user1, user2, entity) while retaining transactionality and the ability of either user to cancel the transfer.

          • elric a day ago

            Permissions.transfer(x, y, z)

            I'm not sure why having a global function by the same would make this any easier or harder to implement. But it would pollute the global namespace with highly specific operations.

            • javcasas a day ago

              And now you have invented a helper class when all you needed is a namespace. And you already had namespaces.

              • elric a day ago

                It doesn't have to be a class. It can be an interface. And regardless of what it is, it has to go somewhere. Whether it's in assorted_functions.c or Permissions.java makes little difference.

                I mean it makes sense to group "like" things together. Whether that's in a module, a class, an interface, or a namespace. Having a huge number of things globally is just confusing pollution.

                • javcasas a day ago

                  > it has to go somewhere

                  Yes, but going as a static method into a class that goes into a module is overkill vs just putting it in the module.

                  > Having a huge number of things globally is just confusing pollution.

                  I don't know what language you use, but modern programming languages allow specifying what to import when importing a module. You don't have to import everything from every module.

        • vips7L a day ago

          It is not a feature. Every programming language since has decided this was a mistake.

          • elric a day ago

            Can you provide an example of that?

            • fmjrey a day ago

              Here is an example of a 2006 rant that qualifies: https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo...

              OO conflates many different aspects that are often orthogonal but have been conflated together opportunistically rather than by sound rigor. Clearly most languages allow for functions outside classes. It's clearly the case today especially with FP gaining momentum, but it's also clear back then when Java and the JVM were created. I think smalltalk was the only other language that had this limitation.

              Like others in this thread, I can only recommend the big OOPS video: https://youtu.be/wo84LFzx5nI

    • 21asdffdsa12 a day ago

      You are so ready : https://fsharpforfunandprofit.com/rop/

      The separation of functions and records..

      • ejflick a day ago

        I've read his book "Domain Modeling Made Functional" without much prior knowledge of F#. He provides some compelling examples and some of it ended up inspiring how I write OO code. F# seems cool but it felt like it was close to being extinct.

        • vips7L a day ago

          Honestly one of the best books I've read about programming. It's inspired me to try the language out just for fun things, think I'm going to use it for Advent of Code this year. I think it does benefit from being interoperable with C# so you can use it where it makes sense and still have C# to fall back on. Kind of like Scala/Kotlin with Java.

    • morshu9001 2 days ago

      Java is a waste of time for the reasons you said. People use it for legacy reasons. Back then, the alternatives like JS just weren't there yet in several spaces like backend. Your alternatives were even more cumbersome like C++.

      • jpnc a day ago

        > People use it for legacy reasons

        This is so incredibly wrong it must be a troll.

  • fmjrey a day ago

    OO fatigue is a healthy symptom of readiness to move to clojure, where data and functions are free to live without encapsulation. No king of nouns, no king of execution!

  • elric a day ago

    Why did you create an anemic domain model?

    Java has had "data carriers" in the form of records for a while now. Immutable(ish), low boilerblate, convenient.

        record User(String name){}
    
    Records are great when doing more "data oriented programming".
    • vips7L a day ago

      I don't think anyone sets out to make an anemic domain model, it just happens. Lots of developers start with POJO's for JPA models and then never advance them into being full fledged objects when the requirements develop.

      • elric 18 hours ago

        Surely that's a conscious design decision? Deciding to create data-carrying POJOs with JPA annotations is a valid strategy. Mixing in a bunch of logic and non-JPA state with them is a recipe for disaster. If you want your classes to Do Stuff, you have to design them to Do Stuff.

        I dislike the term "anemic domain model", it casts a value judgment which I think is unwarranted. There's a spectrum from anemic to obese (for want of a better word). There are tradeoffs all along that spectrum. Finding a sweet spot will depend heavily on what you're doing, why you're doing it, what your team is comfortable with etc.

        • vips7L 14 hours ago

          I genuinely believe its not a conscious design decision. Most people never properly learn how to turn things into objects. I've seen this happen in many projects across many companies. They simply just write procedural code without centralizing their logic anywhere.

    • morshu9001 a day ago

      That's not object oriented though

      • elric 18 hours ago

        Does it have to be? Java is a hybrid paradigm language. It's perfectly fine to write data-oriented code. And if you're using an anemic model, there's no point paying the overhead price of fully fledged classes.

        • morshu9001 18 hours ago

          I meant, if you want to evaluate OOP by itself, you can't use things that break the paradigm. Java broke that probably because they realized OOP isn't always a good idea, after sticking to it up through v7.

mrkeen a day ago

> Another criticism comes from functional programmers, who argue that you don’t need to maintain invariants and thus don’t need much information hiding if data is immutable.

Yep

> Information hiding also encourages people to create small, self-contained objects that “know how to handle themselves,” which leads directly into the topic of encapsulation.

This is where it all goes wrong. No module is an island. There's always relationships between different objects/modules/actors in your system.

Who delivers a letter: Postman or Letter? Who changes a light globe, Handyman or LightGlobe?

Things rarely handle themselves - and if they do, it's probably just a pure function call - so why use Objects?

If you start bending over backwards to implement Letter.deliver(Postman p) (and then make it "general" by changing it to IPostman) you'll never stand up straight again. What if I have a List<Letter>, where does the deliver() code go now?"

If you instead write Deliver(Postman p, Letter l), the opportunities to rewrite/refactor/batch just present themselves.

discreteevent a day ago

"The notion of an interface is what truly characterizes objects - not classes, not inheritance, not mutable state. Read William Cook's classic essay for a deep discussion on this."

- Gilad Bracha

https://gbracha.blogspot.com/2022/06/the-prospect-of-executi...

http://www.cs.utexas.edu/~wcook/Drafts/2009/essay.pdf

  • mrkeen a day ago

    Let's make the interface so special that it gets its own file type. Let's say '.h' for "header".

  • elric a day ago

    There's this quote from Robert C Martin (Uncle Bob)

    > Structured Programming imposes discipline on direct transfer of control. Object Oriented Programming imposes discipline on indirect transfer of control. Functional programming imposes discipline upon assignment. Each of these paradigms took something away. None of them added any new capability. Each increased discipline and decreased capability.

    The interface (or extensible class) enables safe indirect transfer of control.

Mikhail_Edoshin 2 days ago

There is a good methodological principle stated by a Soviet philosoph Ilyenkov: to understand the nature of a thing build or describe a minimal working model of the thing. So simple that if you remove any single detail it ceases to work. He himself gave an example of radio: to understand what radio is build a minimal radio sender and receiver of three or four details each. Do not start with a household radio unit that comes in a polished box with lights and knobs; it has way too many unrelated details.

This works very well both for concrete things like radio and for more abstract things like math or Marx's notion of private property. This is also the principle employed by religious and mystical parables or the book "A pattern language".

  • sph a day ago

    Minimal working model of object orientation, that cannot be simplified further? Look no further than Piumarta’s https://piumarta.com/software/id-objmodel/

    As beautifully shown in that paper, all you need is a primitive “message send” operation and a “lookup” message, pretty much everything else in OOP isn’t necessary or can be implemented at run-time.

aboardRat4 a day ago

People who hate OOP just didn't learn CLOS.

  • elric a day ago

    That would be the Common Lisp Object System. And not using undefined abbreviations might help popularise it.

masfoobar a day ago

> The industry and the academy have used the term “object-oriented” to mean so many different things. One thing that makes conversations around OOP so unproductive is the lack of consensus on what OOP is.

There has been different terms and meaning to it - but we all know the "OOP" thrown about since the mid-to-late 90s is the Java way.

A typical Java book back then would have 900 pages.. half of which is explaining OOP. While not focusing fully on Java, it does help transition that knowledge over to Delphi or C++ or.. eventually.. C#, etc.

Overall -- we all knew what "Must have good OOP skills" means on a job advert! Nobody was confused thinking "Oh.. I wonder which OOP they mean?"

I have a love/hate relationship with OOP. If I have to use a language that is OOP by default then I use it reasonably. While the built in classes will have theor own inheritence -- I tend to follow a basic rule of no higher that 2. Most of the time it is from an interface. I prefer composition over inheritence.

In C#, I use static classes a fair bit. In this case, classes are helpful to organise my methods. However, I could do this at a namespace level if I could just create simple functions -- not wrapped inside a class.

OOP has its place. I prefer to break down my work with interfaces. Being able to use to correct implementation is better than if/switch statements all over the place. However, this can be achieved in non OOP languages as well.

I guess my point is that OOP was shoved heavily back in the day. It was shutup and follow the crowd. It still has it's place in certain scenarios - like GUI interfaces.

NoSalt a day ago

> "OOP-bashing seems fashionable nowadays."

Really, is this happening??? From the job listings I have seen, this is not so.

  • Jtsummers a day ago

    Job listings (written by potential employers) and programming culture are distinct things.

    Bashing OO has been popular since I was in college 25 years ago, but it's also been part of nearly every job I've had except a few embedded systems (Fortran, C).

Symmetry a day ago

On the encapsulation issue, I've come to think that the natural unit of encapsulation is the module rather than the object.

morshu9001 2 days ago

OOP basically means Java or things like Java. Not how it started ofc, but that's what it's been for decades. Minus the lambdas and stuff they added relatively later to compromise on OOP.

  • stOneskull a day ago

    i think of it as a focus (oriented) on instances of something.. class, type, struct..

jauntywundrkind 2 days ago

I'd love a deeper dive on how Objects work in NeXTSTEP. From their brochures, they talk about objects being persistent and distributable, that in a workgroup of people everyone can rely on the objects being up to date.

I've always been so curious what the broader technical ecosystem looks like here. Presumably there are still processes running on systems. But these processes have lots of objects in them? And the objects are using Mach message passing to converse with other processes elsewhere? Within an application, are objects communicating across Mach too?

There's so much high level rhetoric about. Such as this bit. But I'd love a real technical view at what was happening, what objects really were here. https://computerhistory.org/blog/the-deep-history-of-your-ap... https://news.ycombinator.com/item?id=42111938

This is a fun work. It feels like the brief outline for a Speaking for the Dead for OOP. Huge amount of things to lots of different people over time.

Seconding @rawgabbit's recommendation for Casey Muratori's The Big OOPs: Anatomy of a Thirty-five-year Mistake, which really is hunting back and back for the cosmogenesis of objects, and covers so much terrain. Objectogenesis? https://youtu.be/wo84LFzx5nI

tracker1 2 days ago

Commenting while reading...

On classes, I get it... tbf though I'm fine with prototype inheritance as well, there's positives and negatives to both approaches... not to mention, there are benefits to not really having either and just having objects you can interrogate or even that are statically assigned at creation (structs).

What's funny on the Method Syntax for me, is that I actually don't like mixing classes that hold data and classes that do things more often than not. I mean, I get the concepts, but I just don't generally like the approach. The only exception might be a controller with a handle to a model(state) and the view... but even then, the data itself (model) is kind of separated as a reference, and don't tend to attach too many variants of state to anything... I'm generally a fan of the single state tree approach (often used for games, and famously via Redux).

On information hiding... I'm generally not too much of a fan of hiding members of an object used to hold data... I mean, I can see filters when you're passing something to the edge of a system, like a hashed password on a user object exposed via an api. But internally, I'd almost rather see immutability as a first class over locking bits and pieces down, then exposing member methods to mutate the object internally. Just my own take.

On Encapsulation, like above... I'm more on the side of the Data oriented design approach. To me this is where you have API surfaces and like above I tend to separate modules/classes that do things, from templates/models/classes that hold data.

I'm mixed on Interfaces.. they're definitely useful for plugin systems or when you have multiple distinct implementations of a thing... but after a couple decades of C#, they're definitely overrated and overused.

No strong opinions on Late Binding pr Dynamic Dispatch... other than I do appreciate it at times in dynamic language environments (JS).

Inheritance and SubTyping imo are, similar to Interfaces, somewhat overrated... I just try to avoid them more than use them. There are exceptions, I'm actively using this in a project right now, but more often than not, it just adds undue complexity. With prototype based inheritance, it's also possible to really slow down certain processes unintentionally.

Strong proponent of Message Passing approaches... it often simplifies a solution in terms of the surface you need to be aware of at a given point. Allows you to construct decision trees and pipelines of simpler functions.

Interesting overall... but still not a fan of some of the excesses in OOP usage in practice that I've had to deal with. I just prefer to break problems up slightly differently... sometimes blurring clear lines of separation to have a simpler whole, sometimes just drawing the lines differently because they make more sense to me to break up for a given use case.

aussieguy1234 a day ago

One may argue xml is superior to json, which it is.

But json wins out because it can be learned much more quickly.

Something similar could be said with OOP vs Functional Programming.

  • bazoom42 a day ago

    So which one is the quickest to learn? I think Python is easy to learn and C++ is hard, and Scheme is easy and Haskell hard.

    • ebonnafoux a day ago

      Python can be somehow consider OOP since evrything inside it are Object, even function and module.

MarkusQ 2 days ago

I expected this to be a play on the old joke about Java being designed to appeal to people who were into SM/B&D.

twintwinetwolf a day ago

[flagged]

  • llbbdd a day ago

    HN doesn't allow image replies, but if you were to image search "smug meme" you would find any of them to be an appropriate response to this useless post of yours, and imagine I put it here for your convenience

    • twintwinetwolf 15 hours ago

      Hilarious that you're literally trying to outsmug me, and failing. THe irony. It burns. lol