Hacker News new | ask | show | jobs
by sirwhinesalot 29 days ago
The Alan Kay variant of OOP is superior in this regard, and I'd argue the Erlang variant even more so (Actors are just asynchronous objects).

But both of them are still suboptimal because they allow objects to instantiate one another and to directly communicate with whomever they come in contact with. That creates strong coupling and you have to "fight the paradigm" to avoid it.

Other engineering domains have this figured out: components can only send and receive data through ports, they are never aware of their siblings. Only a component at a higher level of abstraction can decide how the ports of lower level components connect to one another. That completely eliminates coupling.

(You can of course replicate this type of architecture with OOP, but you can also replicate it with any turing complete paradigm, that's neither here nor there)

2 comments

Linda and Syndicate figured this out - it’s just that most engineers are not programming language designers or researchers, and most researchers are not designing robust scalable language implementations.
It's crazy to me that Linda came out 40 years ago. I think our field is completely blind to what was achieved in the past.

Smalltalk is to me the most obvious case where people somehow don't realize we had a fully live environment, where the entire IDE (or really the whole operating system) could have open heart surgery done on it while it was running, in the late 1970s!

And that's a language that is at least somewhat in the public consciousness, languages like Linda? Nobody knows them. Synchronous languages like Lustre and Esterel? Some engineers know them (and pay good money for them), but not software engineers.

I have fond memories of bricking my Smalltalk environment several times trying to get instance behaviour working :-)
Thank you for pointing me to Linda -- fascinating and my mind is popping with fun projects related to this model. It actually seems quite spiritually similar to "Bank Python" (https://calpaterson.com/bank-python.html), but more elegant.
Linda: https://en.wikipedia.org/wiki/Linda_(coordination_language)

By Syndicate you mean syndicated actors?

Exactly, the work of Tony Garnock-Jones: https://syndicate-lang.org/
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 is going. They cannot instantiate other blocks.

To create a new block you either create a primitive one (described directly in terms of differential equations, since these are physical simulations, or as actual code, be it C or MATLAB or whatever else), or you connect blocks together to form a larger block.

This creates a strict hierarchy, connections between blocks can only happen at the same level of abstraction. You can just rip out a component and replace it with another with the same ports, or with multiple components each filling in part of the job, or you can plop another component in the middle that's only used to process the data inbound and redirect it somewhere else.

This makes coupling extremely loose by design. The paradigm enforces 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.
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.

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)
No it is not a fault of OOP, it is just that OOP does not really provide the tools to help either. It is the fault of people with the wrong expectations or of those trying to sell snake oil.

OOP allows to create decoupled systems but it doesn't help in creating them. It is not an inherent property of the paradigm, but many people sold it as such.

"Use OOP and your application is now magically more modular! Buy my latest book to find out how!"

Since you mentioned you are a junior I guess this sort of nonsense is from before your time. No worries, that just means you won't have stupid dogma shoved down your throat. Use whatever paradigm if it helps you and avoid whatever gets in the way.