Live data from Hacker News

Alan Kay on the meaning of "object-oriented programming" (2003)

notes.shixiangxi.com

71–80 of 95 posts

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#71

IMO, the most important philosophy in all of software engineering is "Separation of responsibilities." The best way to achieve it is through the principle of "High cohesion, loose coupling." OOP is just another layer of philosophy which builds on top of that. It's more specific, imposes additional guardrails. It requires objects with state encapsulation (locality) and message-passing as the mechanism for components t…

I agree "high cohesion, loose coupling" is a good architectural property to strive for, but OOP is terrible at it and there's no reason to use it for this. Trying to achieve "high cohesion, loose coupling" in OOP has led to the creation of (supposedly) best practice recommendations like the SOLID principles, and the development of monstrosities like dependency injection frameworks. If OOP was actually good at "high c…

There are definitely cases where you could have a module with distinct responsibilities; so you can definitely get high cohesion and loose coupling without OOP, that's true, but there are cases where you may want to:

- Control the timing of when a module is activated (instantiated).

- Have multiple instances of a module with variations in functionality where those variations are not a concern to the parent module/instance.

For me, this is when OOP becomes most useful. If I can write some code once and later use it to create any number of independent instances with the same functionality which can clean up after themselves, this is generally a lot more maintainable than having one module to keep track of all the different states in an array and micromanaging (for example) the rendering and cleanup work associated with multiple distinct pieces of state.

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#72
post #13

Earlier quoted context omitted.

In fact, to a certain point of view, OOP is a way to have modules bind to variables, and being extensible.

And the price for this is constrained flexibility and utility. Data has value and utility on it's own --- as much as if not more than code that manipulates it. It is often not possible to foresee all the ways it can and will need to be used. So why bind anything to it? Relational databases were invented to provide flexibility with regard to data. OOP tries to encumber it --- often referred to as an "impedance mismatc…

Proper designed modules also don't expose more than the essentially necessary on their public interface.

Failure to understand this is what leads many into microservice hell, as they try to impose a OS process wall as solution to having failed to properly design a modular architecture.

So what could perfectly be a module with direct calls, ends up being a process talking over IPC mechanisms and much higher latency, with the added fun of distributed systems.

Ironically pretty much OOP without any of the language support.

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#73

Earlier quoted context omitted.

I agree "high cohesion, loose coupling" is a good architectural property to strive for, but OOP is terrible at it and there's no reason to use it for this. Trying to achieve "high cohesion, loose coupling" in OOP has led to the creation of (supposedly) best practice recommendations like the SOLID principles, and the development of monstrosities like dependency injection frameworks. If OOP was actually good at "high c…

There are definitely cases where you could have a module with distinct responsibilities; so you can definitely get high cohesion and loose coupling without OOP, that's true, but there are cases where you may want to: - Control the timing of when a module is activated (instantiated). - Have multiple instances of a module with variations in functionality where those variations are not a concern to the parent module/ins…

To be clear I'm not arguing against objects here, I don't disagree with anything you wrote. I'm only arguing against the somewhat commonly held idea that OOP helps with loose coupling.

Interfaces (a feature of OOP) can be used to decrease coupling, but the paradigm itself doesn't really provide any assistance in regards to coupling.

The default is for objects to directly instantiate other concrete objects and to be able to directly communicate with any object they at any point come in contact with. That is strong coupling, by default.

You have to do extra work to get looser coupling. Smalltalk style OO is much better (regarding coupling) because you don't have explicit interfaces, you can always replace an object with another. You can also query all live objects of a certain type and replace all of them with a proxy if you so wish. Way better.

But you can go even further in regards to loosening coupling. You can have objects communicate through a tuplespace (Linda), you can have objects receive and send messages only through ports, you can have objects send a message to their implicit parent who is then responsible for redirecting it.

Mainstream OOP is a very poor paradigm full of issues that gets mogged by everything. Composition-over-inheritance is an admission of defeat for a paradigm that lacks native delegation support (good on Kotlin for realising this).

It even gets mogged by languages like Haskell that introduced the more loosely coupled idea of typeclasses, which were inherited by Rust (traits), Swift (protocols), Go (interfaces, not to be confused with declaration-time interfaces as in Java).

We dug a suboptimal hole and stuck our head in there for 50 years.

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#74
post #55

Earlier quoted context omitted.

When you say other engineering domains have figured this out, can you give a more specific example?

Yes, for example if you are doing any sort of mechatronic systems engineering (cars, planes, rockets, etc.), you're typically using tools like Simcenter Amesim or Modelica or Simulink/Stateflow to digitally prototype the system. These tools are all oriented around blocks with ports. They receive data on their input ports and emit data on their output ports. They do not know where that data is coming from nor where it…

'in->func()->out' work cleanly for physical engineering domains because blocks of computations (or functions) have no memory basically. There's no DB and there's usually no global state in the way you'd mean with classes. I've observed that when you work on complex software, you could play by the ear to enforce 'high cohesion and loose coupling' up to the point to where you've now got a data store holding system state. Based on what I've worked on, this system state is actually user data and then some derivative of the system's own interaction on this user data, both of which are necessary for the 'continuity of system runtime'. For example, working with redis as the primary data store, I extracted different types of redis calls for specific keys, (like set [specific_key], get [specific key], etc.) into functions, then I put these functions into a StateStore class so I can simply call StateStore.get_user_data() or StateStore.set_user_data() etc from any module across the entire system. From first principles, this is great modularity and high cohesion but it's very tight coupling around a single module i.e. StateStore. Any change in that module means I'd have to find all references for that updated function and cross check for any contract violations. It's difficult for me to see how software can purely be 'High Cohesion and Loose Coupling' regardless of paradigm or architectural pattern. There's always gonna be an unavoidable and inevitable principle violation that's simple a result of a large complex system actually doing many little things that come together to actually do one big thing. The software itself, no matter how complex, IS the blackbox where data goes in and something comes out but that's the user perspective not ours as the programmer. I think the idea I'm trying to hint at is that software cannot have every module independent of every other one. That's impossible. If nothing depended on anything else, the system wouldn't do anything. Rather than have every module know redis keys, States tore actually concentrates coupling into one place, which is exactly what high cohesion tries to accomplish. I think that's the idea I'm chasing.

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#75

Earlier quoted context omitted.

There are definitely cases where you could have a module with distinct responsibilities; so you can definitely get high cohesion and loose coupling without OOP, that's true, but there are cases where you may want to: - Control the timing of when a module is activated (instantiated). - Have multiple instances of a module with variations in functionality where those variations are not a concern to the parent module/ins…

To be clear I'm not arguing against objects here, I don't disagree with anything you wrote. I'm only arguing against the somewhat commonly held idea that OOP helps with loose coupling. Interfaces (a feature of OOP) can be used to decrease coupling, but the paradigm itself doesn't really provide any assistance in regards to coupling. The default is for objects to directly instantiate other concrete objects and to be a…

I think OOP helps to build loosely coupled systems but it doesn't protect you from tight coupling. You have to know what you're doing.

I think OOP languages made some pragmatic decisions. Sometimes a feature which is harmful 95% of the time could be genuinely useful and safe 5% of the time... Some languages like Haskell might choose to not allow that feature at all and force the developer to find another approach which is almost as effective for that 5% of cases; that's fair enough. It's a different philosophy.

I feel like that about passing mutable objects by reference. I find it harmful most of the time but there are rare cases were it's convenient and beneficial. I've worked on open source projects were I wanted the user to be able to use the software with any database so my function accepted a database adapter as an argument.

I could have achieved a similar goal in another way but I would have had to sacrifice separation of concerns slightly. I wanted the ability to substitute any database but also wanted the component to be responsible for the persistence and recovery of its own state as this was within its responsibilities. Also this was the only violation in the entire codebase so I deemed it acceptable. It didn't pose any problems at all in practice.

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#76

Earlier quoted context omitted.

Yes, for example if you are doing any sort of mechatronic systems engineering (cars, planes, rockets, etc.), you're typically using tools like Simcenter Amesim or Modelica or Simulink/Stateflow to digitally prototype the system. These tools are all oriented around blocks with ports. They receive data on their input ports and emit data on their output ports. They do not know where that data is coming from nor where it…

'in->func()->out' work cleanly for physical engineering domains because blocks of computations (or functions) have no memory basically. There's no DB and there's usually no global state in the way you'd mean with classes. I've observed that when you work on complex software, you could play by the ear to enforce 'high cohesion and loose coupling' up to the point to where you've now got a data store holding system stat…

It's not really true that components in physical engineering domains have no memory. The behavior of many physical components can only be described with access to a recent history. Other components, specially controllers, are typically state machines (which obviously have state).

But it is true that there will always be parts of a software system that are highly coupled. I'm not even sure if that's even a problem, unless the coupling is also highly tangled (meaning the connections are coming from too many places).

But if you want to decrease coupling between parts of a system, OOP by itself is not particularly good at it. Even something like an Entity Component System is usually less coupled than most OOP codebases because while each System is heavily coupled to the components they do work on, the Systems are usually fully independent from one another and easily replaceable.

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#77

Earlier quoted context omitted.

To be clear I'm not arguing against objects here, I don't disagree with anything you wrote. I'm only arguing against the somewhat commonly held idea that OOP helps with loose coupling. Interfaces (a feature of OOP) can be used to decrease coupling, but the paradigm itself doesn't really provide any assistance in regards to coupling. The default is for objects to directly instantiate other concrete objects and to be a…

I think OOP helps to build loosely coupled systems but it doesn't protect you from tight coupling. You have to know what you're doing. I think OOP languages made some pragmatic decisions. Sometimes a feature which is harmful 95% of the time could be genuinely useful and safe 5% of the time... Some languages like Haskell might choose to not allow that feature at all and force the developer to find another approach whi…

I think dogma is far worse than "tight coupling" or introducing mutation or even inheritance when it is the simplest and most direct solution to a problem. People get too hung up on the "ideal" way to do things.

I can criticize a paradigm while also using it anywhere and everywhere it benefits me ;)

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#78
post #49
post #21

I studied the history of OOP a while back because I was curious, and I organized what I learned into my personal wiki[1]. From what I remember, there were quite a few different perspectives on it. One view traces OOP's practical ancestry back to Ole-Johan Dahl's Simula. From Alan Kay's perspective, on the other hand, an object was something like a small computer of its own. The two main lineages of OOP are Simula and…

You might like Casey Muratori's "The Big OOPs" talk about this, if you haven't seen it yet: https://www.youtube.com/watch?v=wo84LFzx5nI It's my favorite deep dive into the subject, it's super thoughtful and well structured.

This comment was dead for some reason; I vouched for it. Casey Muratori is a well-known developer with a GUI/game engine background, which gives him a valuable perspective on OOP. In both domains, OOP is both popular and problematic, and his experience suggests that he might know what he's talking about. I didn't watch the whole video, but from the description and intro, it seems to fit the discussion we're having here.

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#79

Earlier quoted context omitted.

Behavior is a good clue. The current model, even with data, lacks behavior. It has a method, which is like a message we send to it. But it does not seem to give it enough behavior, even though we don't make any assumptions about its complexity. Or maybe it could give it behavior if it were like that: class Aaaa [some data] method handle(message, ...) [some code] This construction implies there are multiple messages.…

That's quite the thing to bring up. Wonderful. So you say an object is like a computation stretched over time and a function that same computation but compressed into a single invocation? Like an object is a computation whose execution is suspended between messages? I can see how that ties closures, co-routines, etc together. They are all machinery to preserve execution state across time. Generally you could say comp…

Yes, this is exactly what I'm saying. Objects, closures, co-routines and eventually state machines, which was the first concept, I think, all revolve around the same core thing.

We keep returning to it because this is the natural way to do computation using a machine, but we also try to escape because it is rather hard for a human. We like the functional form more: it looks sequential and goes nicely from start to end. With objects we quickly lose our way in a soup of small parts.

Re: Alan Kay on the meaning of "object-oriented programming" (2003)

#80

Earlier quoted context omitted.

'in->func()->out' work cleanly for physical engineering domains because blocks of computations (or functions) have no memory basically. There's no DB and there's usually no global state in the way you'd mean with classes. I've observed that when you work on complex software, you could play by the ear to enforce 'high cohesion and loose coupling' up to the point to where you've now got a data store holding system stat…

It's not really true that components in physical engineering domains have no memory. The behavior of many physical components can only be described with access to a recent history. Other components, specially controllers, are typically state machines (which obviously have state). But it is true that there will always be parts of a software system that are highly coupled. I'm not even sure if that's even a problem, un…

Yeah you're right. It's an oversimplification to say components in physical engineering domains have no memory. I was thinking about how logic gates are reasoned about. But that's not the exact point... It's that the argument is usually that OOP cannot decrease coupling. I can't see how exactly. OOP seems to strike a balance between good enough modularity and expressive power enough to keep your system directionally loosely coupled. Infact, I'd say that, I my experience, lots of the coupling between modules is inherent to the 'specifics' of the system's design requirements itself. This is where sometimes within the same logical subsystem/component you could find multiple design patterns all deployed in the bid to reduce coupling. I don't think this is OOP's fault directly as opposed to the 'devil that is in the details'. But maybe I don't have enough experience or exposure yet (I'm a 2nd-year junior)
Post reply on HN