Rendered at 11:30:07 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
jdw64 3 hours ago [-]
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 Smalltalk. But from what I recall, modern languages actually inherited more from the Simula side in practical terms, while the terminology and philosophy were influenced more by Smalltalk.
[1]https://www.makonea.com/en-US/wiki/object-oriented-programmi...
Which corresponds pretty well with the Simula I concept, published 1966 in the Communications of the ACM. A Simula event notice (time, process) in the sequencing set is just a message step(process, time) in a priority mailbox; the two are the same mathematical object, making Simula's discrete-event active processes and Kay's message-passing active objects trivially isomorphic.
jdw64 2 hours ago [-]
thanks!
I checked the link and there are indeed quite a few inaccuracies in the initial part. I'll go through it and make corrections.
He does research in "the history and philosophy of programming", amongst other things.
shevy-java 1 hours ago [-]
Depends on the language. I would argue that ruby inherited more from Smalltalk than from Simula, for instance.
Rochus 4 minutes ago [-]
“I wanted a scripting language that was more powerful than Perl, and more object-oriented than Python" and "Ruby's class library is an object-oriented reorganization of Perl functionality--plus some Smalltalk and Lisp stuff" (see "An Interview with the Creator of Ruby", 2001, https://web.archive.org/web/20041220041220/http://www.linuxd...).
tskj 4 minutes ago [-]
[dead]
paolfs 47 minutes ago [-]
I think the actor model comes closest to Kay's objects.
An object holds it's own state and might change that state based on messages it receives.
Today you can find this in, for example, Elixir and Microsoft Orleans.
socketcluster 4 hours ago [-]
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 to interact with each other; each component is responsible for changing a subset of the state of the system. Each component is responsible for handling messages (calls to action) by performing local state changes and potentially also sending messages to other components which have more specific sub-responsibilities.
OOP without "High cohesion, loose coupling" is almost worthless IMO. It must build on top.
I think were most people fail with OOP is that they think coming up with good separation of concerns, good abstractions is easy. They just start implementing the first idea which comes out of their heads and then figure out the scope of responsibilities as they go.
The test for good separation of concerns is that you should be able to explain your architecture to someone with the intellect of a 9 year old child who happens to understand the business domain. I'm not exaggerating. It has to be that obvious or else you will not be able to maintain clean separation... You will not be able to maintain alignment in your team.
If the responsibilities of a specific object are somewhat vague, what will happen is that the scope of responsibilities between objects will soon blur and the messages between objects will start to look increasingly elaborate; your system will look like a bunch of horrible incompetent managers trying to micromanage junior employees using long, convoluted instructions and occasionally throwing chairs at them...
If your system is passing around object references all over the place; that's usually a sign of poor separation of concerns; passing around complex objects by reference is tight coupling, by definition. Each object, each person should be able to fulfill their responsibilities and finish the job using communication only.
sirwhinesalot 3 hours ago [-]
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 cohesion, loose coupling", you wouldn't need them.
KayEss 2 hours ago [-]
I think this is looking at what OOP has become (as implemented by systems and programming languages that don't care a wit about what OO was meant to be) rather than what Alan Kay described.
If you think about something like a web server as an object, it has arbitrarily high cohesion and arbitrarily low coupling. You can only communicate to it through messages (HTTP); binding happens at the point in time that the message arrives (notwithstanding that this may be cached in all sorts of interesting ways in the implementation of any given server); and the web server is fully encapsulated (security flaws notwithstanding).
I think it's perfectly reasonable to argue that much of what gets called OOP doesn't deliver on the promise, but then it doesn't deliver on the premise either, and I think these are inextricably linked.
sirwhinesalot 1 hours 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)
Kinrany 52 minutes ago [-]
I dislike OOP as much as the next HN commenter, but dependency injection tools are good in principle. OOP just uses them much more and for bad reasons.
sirwhinesalot 6 minutes ago [-]
Dependency Injection frameworks are a workaround for limitations of the paradigm, much like Design Patterns also are. They're necessary (by a very stretched definition of necessary) because people want to do certain things and the paradigm is fighting them.
Some part of the code wants to do logging. The paradigm-native solution is to just instantiate a logger object directly, but that's not really what you want to happen, because there are application-level concerns as to where those logging messages should go.
So instead of a direct new, you create a Logger interface (extra work), then use the Factory pattern (extra work) to hide the concrete logger objects that get instantiated. Ok, but now you are still creating multiple independent loggers, which will trample on each other.
So you employ the Singleton pattern (extra work), but now you can't use different loggers for different parts of the codebase.
Ok, let's instead have the application instantiate the proper loggers, and then thread them through method arguments as needed (extra work).
But now passing all those loggers around is super annoying and extremely noisy, so you create a framework that can inject them where they are needed.
Great, now you have a whole framework just to pass some logger objects around. The framework is not the problem per se, it exists for a reason, the problem is that someone felt the need for it. And it is a massive pile of complexity that is NOT worth it.
bluGill 23 minutes ago [-]
OOP isn't why they are used. OOP may enable injection more than others, but it is not encouraged. Bad programmers and bad architects is why it is overused not OOP. Keep the blame in the right place.
Fire-Dragon-DoL 3 hours ago [-]
This resonates strongly with me, which is why I find more important having some form of namespacing than anything else.
OOP has been muddied so much too, it would be interesting if we taught only the "separation of responsibilities" part and principles related to that.
Often it comes up: who has write authority, that's something we don't teach as part of OOP, yet realizing that only one thing should do writing improves the code dramatically.
Now that you see it, seems so obvious
_pdp_ 2 hours ago [-]
> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.
This sounds a lot like microservice architecture.
Twey 34 minutes ago [-]
Taken to an extreme. It's not feasible with current microservice architecture to, for example, represent every Boolean in the program as a service.
TZubiri 2 hours ago [-]
I too think that microservices and REST are Kay OOP, although through a network boundary of course
The separation of objects and usage of network protocols naturally forces interaction via communication and not data sharing, so perhaps as an emergent phenomenon it converges on Kay OOP. Although no doubt Netflix Microservice OG engineers were influenced by both Kay and Java.
jqpabc123 2 days ago [-]
I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages
It was originablly conceived as a simulation of a distributed system.
Distributed systems can be useful but does anyone really believe that they are simpler or easier to develop and maintain?
The amazing part to me is that so many were trained and convinced to accept that adopting this simulation could make all programming easier or somehow "better". As if adding complexity would magically lead to simplification.
sph 7 hours ago [-]
> Distributed systems can be useful but does anyone really believe that they are simpler or easier to develop and maintain?
I believe that programs written in languages made for distributed systems are simpler and easier to maintain.
Also I believe that one of the major problems in modern computing is that most languages we use do not understand that even trivial programs require ‘communication’ and OS/hardware facilities that have behaviours of distributed systems such as latency, transient faults, etc.
BobbyTables2 7 hours ago [-]
Generally speaking, toy examples can always be made to look simple. In reality it often means the “hard part” was moved into the shadows, not actually solved.
Graphical programming like LabVIEW looks very appealing. The product demo practically sells itself. Sure, it fits well for a very narrow class of use cases. But even fairly simple things in a textual language quickly become an unwieldy mess. (Try factoring an integer in it…)
There are formal models for distributed systems often solve the “easy” problems that didn’t need solving while making various practical concerns harder/impossible such as timeouts or node failure.
agumonkey 3 hours ago [-]
Just a guess, it may be that he understood distribution more as the static distribution of meaning in fine grain entities (objects) to allow a wide range of context, flexibility. Not necessarily the time aspect of distribution.
shevy-java 58 minutes ago [-]
> Distributed systems can be useful but does anyone really believe that they are simpler or easier to develop and maintain?
Dinosaur. Whale.
I think these were success stories for a long time. So, it is possible to develop and maintain complex systems.
TZubiri 2 hours ago [-]
>Distributed systems can be useful but does anyone really believe that they are simpler or easier to develop and maintain?
If you try to maintain a decentralized system, it will be hard yes.
However if you see the nirvana and try to maintain the parts of the system, it will be easier.
TZubiri 2 hours ago [-]
Foundational source nowadays. Cited by wikipedia on the topic of OOP
I have personally taken to calling OOP either Kay OOP or Java OOP to differentiate between the original more philosophical meaning, and the later language imposed features.
ahartmetz 2 days ago [-]
That's the Smalltalk school of OOP. There is also the Simula school. It is kind of unfortunate that they use the same name.
Rochus 2 hours ago [-]
> That's the Smalltalk school of OOP
In particular the "Smalltalk-72 school" which had indeed something like "message passing" (though still synchronously). Starting from Smalltalk-76, and particularly in Smalltalk-80, which is the Smalltalk we know today, the object model pretty much corresponds to Simula-67, with compiled methods dipatched via virtual method tables. The only difference is, that in Smalltalk, the dispatch goes via the internalized string address of the selector (vs. method index as e.g. in Simula-67, C++ or Java). See e.g. https://dl.acm.org/doi/10.1145/3386335.
mycall 2 days ago [-]
> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.
How does Simula differ here?
ahartmetz 2 days ago [-]
AFAIU, Simula focused more on types and inheritance and less on late-binding, in particular not of "all things".
Alan Kay's distaste for (static) types is just his opinion and an original contribution of IMO rather dubious value.
After the dust has settled, it seems like the most valuable parts of OOP are private data, convenience (no need to repeat the class name in a method call), good fit for some domains, and interfaces.
jqpabc123 2 days ago [-]
private data, convenience
Which can be easily achieved without OOP.
pjmlp 5 hours ago [-]
In fact, to a certain point of view, OOP is a way to have modules bind to variables, and being extensible.
pitched 6 hours ago [-]
That upfront convenience here leads to a long tail of job security when it inevitably goes spaghetti. Win-win!
gf000 4 hours ago [-]
Spaghetti ~ complexity ~ entropy.
That's the natural order of things that many people work in, I doubt any "general loosely defined paradigm" can be absent from the inevitable.
Mikhail_Edoshin 6 hours ago [-]
There was a Soviet philosopher, Evald Ilyenkov, whose books taught me about a "minimal working model". I'll explain it in my own words.
People do not think with words: people think with things. Words serve merely as pointers to things. Some things are easy to point at; Ilyenkov talks about a cow. Some things are much harder to point at; Ilyenkov, being a Marxist, wanted to point to private property, we need to point to an object. Usually we try to talk about them using definitions, that is some sequences of words that are supposed to describe a thing. Such discussions are notoriously unproductive. The reason is that words are not really good as pointers: we have much more things than we have words and we have to reuse the same words to point to different things in different contexts. Yet in a phrase words look same so we tend to conflate these different things. As a result we get lost and go around in circles.
So instead of definitions Ilyenkov talks about a notion. Notion is something that reliably points to a thing that is hard to point at. It does not have to be an abstract thing: for example, we cannot point to radio. We can point to a household radio apparat but it has way too many parts completely unrelated to radio as a principle. So instead we build a minimal radio that has like three or four parts yet is capable of emitting or receiving radio waves.
We can do the same with abstract things too using the same approach. Let's build a minimal working model of a thing. "Working" so that the model indeed has the quality we are after; and "minimal" so that if we lose a single part, the quality is gone. Since the thing is abstract the model will also be abstract, that is it will be a sequence of words too, very much like a definition, but not quite.
(The same principle is widely used in parables: they describe some situation and thus try to guide your attention to certain qualities of it, hoping to trigger understanding. Sometimes it is hard to even name the thing they are pointing at, yet their pointing power is palpable. I myself often think about the parable about seven blind people and an elephant. You see I'm not naming the thing it points at: I don't have a good name for it.)
So let's go back to object oriented programming. What would be a minimal working thing that we can reliably call an object?
Here is what cannot be there. First, there must be no inheritance. If inheritance were required, then the first thing we build wouldn't be an object as it would have nothing to inherit from. Only the second thing would be an object and only because it inherited from the first. But this does not seem right. Second, there must be no polymorphism on the same grounds.
But at the same time if we do this:
class Aaaa
method bbbb():
...
then it is too minimal. It is hard to say how it is different from a function. It is not an object at all. Yet; there is a missing part that would turn this into a true object, but what the part is could be somewhat surprising.
sph 4 hours ago [-]
Thank you Mikhail. I'm reading a lot of metaphysics, and thinking hard about the nature of programming and object orientation, so I appreciate your philosophical approach to the problem. I wasn't aware of Ilyenkov.
To answer your question:
> What would be a minimal working thing that we can reliably call an object?
Also check out the primary author's work on COLA as well for mind-bending possibilities this would unlock.
> First, there must be no inheritance.
The paper uses inheritance, but I've been exploring the same concepts WITHOUT it, and it works as well, if not better. Composition and delegation are more flexible. My current working theory is that inheritance is overrated at best, and too dangerous in the hands of common mortals, because it makes you think on the level of idealized categories rather than concrete things.
Your example lacks data. An object is the combination of data and code manipulating the data with some syntactic sugar on top
Mikhail_Edoshin 2 hours ago [-]
Data will indeed be necessary. But data in OOP are interesting: they are not supposed to be directly visible. They are like the method body: there is one, but it is not important what it is. So if we add data, it will be that:
class Aaaa
(some data)
method bbbb():
(some code)
Do you think this is an object now?
usrnm 2 hours ago [-]
Yes, it is an object now, at least in my book. Public/private is less important, there are examples of OOP systems with private data being optional or non-existent at all, for example in Python.
sirwhinesalot 4 hours ago [-]
Another commenter pointed it out already but might as well stress the point: objects are bundles of data and behavior. Your example is missing data.
sdfsdfs34dfsdf 3 hours ago [-]
That's super interesting. Thank you for that.
I believe what's missing is not just data. That'd only grant it capabilities that upgrade it to a "record" type of entity. I believe an OOP object is more than a record. It's missing behavior triggered by messages. For an object to pass or receive a message we need to have a model of a message and that requires the notion of a sender and receiver, both objects again. Seems circular, but I'm sure it could be made to work if you properly define everything. Anyway, to my mind perhaps the minimal model of an object is not at all _one object_ but a _relation_ between two or more objects showcasing the minimal "message passing" semantics.
Weird take, but inheritance could be included if you accept something can inherit from itself. A is a type of A, I mean it doesn't strike me as wrong, but it is unconventional.
Mikhail_Edoshin 2 hours ago [-]
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. "Multiple" is the key difference. The part that was missing in the original sketch is a second method:
Now this is an object. For example, it can be a random number generator: we initialize it and then read next numbers. Or it could be a timer: we initialize it and then read the value. We do not need a fully object-oriented environment for that; there is a plenty of such things in C and other non-OOP systems.
Such a thing surely has some behavior and this is exactly what we use. We are not interested in the internal data much. In fact the internal data of an object play exactly the same role as a function stack frame: it is a private slice of memory a computation keeps for itself because this is how it works. As in knitting the size of the manipulator is tiny compared to the final result and to do anything substantial we need a place to keep stuff around until we are done. And we need to be sure data stay were we've put them, hence encapsulation.
So an object is very much like a function, it is a computation that uses some memory to do its job. It is different in that in an object the computation runs step-by-step guided by external events. A function is like an object that gets the whole sequence of events at once, runs from start to finish and in the end discards the working memory so we tend to forget it exists. An object runs from message to message and keeps the working memory, which we see as "object data". This is why objects arise naturally in areas like user interface where events are truly external. But an object is actually a primary form of computation: if you can process a single event, you can use it to process a sequence; but if you can only process the whole sequence, you cannot just switch to processing single events. There are many similarities with closures, coroutines and such; I'd say they are different ways to express the same principle.
(This also means that objects are naturally mutable. Immutable objects are an aberration that arose because we've got here in a very roundabout way.)
watt 5 hours ago [-]
don't say "apparat" when you can say "device".
side note, when first transistor-based portable radios showed up (as opposed to large lamp-based stationary devices), they were called "transistor". you would take not your portable radio with you, it was your transistor.
shevy-java 1 hours ago [-]
Alan Kay's definition of OOP is my favourite one, though
I actually extended it a bit; for instance, erlang/elixir
kind of have "failsafe, reliable objects". Now, barely anyone
would call these languages OOP, but IMO it follows just logically
similar to Alan Kay's definition. Probably a new language may be
required, which is hard to do (make it a succes, that is hard),
but along those lines of having a wider definition of OOP. What
I definitely hate is the limitation towards C++ and Java. Those
two languages really messed up the OOP term, and then we had
clown languages such as PHP just copy/pasting that definition
and not understanding what OOP is really all about.
The two main lineages of OOP are Simula and Smalltalk. But from what I recall, modern languages actually inherited more from the Simula side in practical terms, while the terminology and philosophy were influenced more by Smalltalk. [1]https://www.makonea.com/en-US/wiki/object-oriented-programmi...
> something like a small computer of its own
Which corresponds pretty well with the Simula I concept, published 1966 in the Communications of the ACM. A Simula event notice (time, process) in the sequencing set is just a message step(process, time) in a priority mailbox; the two are the same mathematical object, making Simula's discrete-event active processes and Kay's message-passing active objects trivially isomorphic.
He does research in "the history and philosophy of programming", amongst other things.
An object holds it's own state and might change that state based on messages it receives.
Today you can find this in, for example, Elixir and Microsoft Orleans.
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 to interact with each other; each component is responsible for changing a subset of the state of the system. Each component is responsible for handling messages (calls to action) by performing local state changes and potentially also sending messages to other components which have more specific sub-responsibilities.
OOP without "High cohesion, loose coupling" is almost worthless IMO. It must build on top.
I think were most people fail with OOP is that they think coming up with good separation of concerns, good abstractions is easy. They just start implementing the first idea which comes out of their heads and then figure out the scope of responsibilities as they go.
The test for good separation of concerns is that you should be able to explain your architecture to someone with the intellect of a 9 year old child who happens to understand the business domain. I'm not exaggerating. It has to be that obvious or else you will not be able to maintain clean separation... You will not be able to maintain alignment in your team.
If the responsibilities of a specific object are somewhat vague, what will happen is that the scope of responsibilities between objects will soon blur and the messages between objects will start to look increasingly elaborate; your system will look like a bunch of horrible incompetent managers trying to micromanage junior employees using long, convoluted instructions and occasionally throwing chairs at them...
If your system is passing around object references all over the place; that's usually a sign of poor separation of concerns; passing around complex objects by reference is tight coupling, by definition. Each object, each person should be able to fulfill their responsibilities and finish the job using communication only.
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 cohesion, loose coupling", you wouldn't need them.
If you think about something like a web server as an object, it has arbitrarily high cohesion and arbitrarily low coupling. You can only communicate to it through messages (HTTP); binding happens at the point in time that the message arrives (notwithstanding that this may be cached in all sorts of interesting ways in the implementation of any given server); and the web server is fully encapsulated (security flaws notwithstanding).
I think it's perfectly reasonable to argue that much of what gets called OOP doesn't deliver on the promise, but then it doesn't deliver on the premise either, and I think these are inextricably linked.
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)
Some part of the code wants to do logging. The paradigm-native solution is to just instantiate a logger object directly, but that's not really what you want to happen, because there are application-level concerns as to where those logging messages should go.
So instead of a direct new, you create a Logger interface (extra work), then use the Factory pattern (extra work) to hide the concrete logger objects that get instantiated. Ok, but now you are still creating multiple independent loggers, which will trample on each other.
So you employ the Singleton pattern (extra work), but now you can't use different loggers for different parts of the codebase.
Ok, let's instead have the application instantiate the proper loggers, and then thread them through method arguments as needed (extra work).
But now passing all those loggers around is super annoying and extremely noisy, so you create a framework that can inject them where they are needed.
Great, now you have a whole framework just to pass some logger objects around. The framework is not the problem per se, it exists for a reason, the problem is that someone felt the need for it. And it is a massive pile of complexity that is NOT worth it.
OOP has been muddied so much too, it would be interesting if we taught only the "separation of responsibilities" part and principles related to that.
Often it comes up: who has write authority, that's something we don't teach as part of OOP, yet realizing that only one thing should do writing improves the code dramatically.
Now that you see it, seems so obvious
This sounds a lot like microservice architecture.
The separation of objects and usage of network protocols naturally forces interaction via communication and not data sharing, so perhaps as an emergent phenomenon it converges on Kay OOP. Although no doubt Netflix Microservice OG engineers were influenced by both Kay and Java.
It was originablly conceived as a simulation of a distributed system.
Distributed systems can be useful but does anyone really believe that they are simpler or easier to develop and maintain?
The amazing part to me is that so many were trained and convinced to accept that adopting this simulation could make all programming easier or somehow "better". As if adding complexity would magically lead to simplification.
I believe that programs written in languages made for distributed systems are simpler and easier to maintain.
Also I believe that one of the major problems in modern computing is that most languages we use do not understand that even trivial programs require ‘communication’ and OS/hardware facilities that have behaviours of distributed systems such as latency, transient faults, etc.
Graphical programming like LabVIEW looks very appealing. The product demo practically sells itself. Sure, it fits well for a very narrow class of use cases. But even fairly simple things in a textual language quickly become an unwieldy mess. (Try factoring an integer in it…)
There are formal models for distributed systems often solve the “easy” problems that didn’t need solving while making various practical concerns harder/impossible such as timeouts or node failure.
Dinosaur. Whale.
I think these were success stories for a long time. So, it is possible to develop and maintain complex systems.
If you try to maintain a decentralized system, it will be hard yes.
However if you see the nirvana and try to maintain the parts of the system, it will be easier.
I have personally taken to calling OOP either Kay OOP or Java OOP to differentiate between the original more philosophical meaning, and the later language imposed features.
In particular the "Smalltalk-72 school" which had indeed something like "message passing" (though still synchronously). Starting from Smalltalk-76, and particularly in Smalltalk-80, which is the Smalltalk we know today, the object model pretty much corresponds to Simula-67, with compiled methods dipatched via virtual method tables. The only difference is, that in Smalltalk, the dispatch goes via the internalized string address of the selector (vs. method index as e.g. in Simula-67, C++ or Java). See e.g. https://dl.acm.org/doi/10.1145/3386335.
How does Simula differ here?
Alan Kay's distaste for (static) types is just his opinion and an original contribution of IMO rather dubious value.
After the dust has settled, it seems like the most valuable parts of OOP are private data, convenience (no need to repeat the class name in a method call), good fit for some domains, and interfaces.
Which can be easily achieved without OOP.
That's the natural order of things that many people work in, I doubt any "general loosely defined paradigm" can be absent from the inevitable.
People do not think with words: people think with things. Words serve merely as pointers to things. Some things are easy to point at; Ilyenkov talks about a cow. Some things are much harder to point at; Ilyenkov, being a Marxist, wanted to point to private property, we need to point to an object. Usually we try to talk about them using definitions, that is some sequences of words that are supposed to describe a thing. Such discussions are notoriously unproductive. The reason is that words are not really good as pointers: we have much more things than we have words and we have to reuse the same words to point to different things in different contexts. Yet in a phrase words look same so we tend to conflate these different things. As a result we get lost and go around in circles.
So instead of definitions Ilyenkov talks about a notion. Notion is something that reliably points to a thing that is hard to point at. It does not have to be an abstract thing: for example, we cannot point to radio. We can point to a household radio apparat but it has way too many parts completely unrelated to radio as a principle. So instead we build a minimal radio that has like three or four parts yet is capable of emitting or receiving radio waves.
We can do the same with abstract things too using the same approach. Let's build a minimal working model of a thing. "Working" so that the model indeed has the quality we are after; and "minimal" so that if we lose a single part, the quality is gone. Since the thing is abstract the model will also be abstract, that is it will be a sequence of words too, very much like a definition, but not quite.
(The same principle is widely used in parables: they describe some situation and thus try to guide your attention to certain qualities of it, hoping to trigger understanding. Sometimes it is hard to even name the thing they are pointing at, yet their pointing power is palpable. I myself often think about the parable about seven blind people and an elephant. You see I'm not naming the thing it points at: I don't have a good name for it.)
So let's go back to object oriented programming. What would be a minimal working thing that we can reliably call an object?
Here is what cannot be there. First, there must be no inheritance. If inheritance were required, then the first thing we build wouldn't be an object as it would have nothing to inherit from. Only the second thing would be an object and only because it inherited from the first. But this does not seem right. Second, there must be no polymorphism on the same grounds.
But at the same time if we do this:
then it is too minimal. It is hard to say how it is different from a function. It is not an object at all. Yet; there is a missing part that would turn this into a true object, but what the part is could be somewhat surprising.To answer your question:
> What would be a minimal working thing that we can reliably call an object?
You might enjoy this paper a lot: https://piumarta.com/software/id-objmodel/objmodel2.pdf
Also check out the primary author's work on COLA as well for mind-bending possibilities this would unlock.
> First, there must be no inheritance.
The paper uses inheritance, but I've been exploring the same concepts WITHOUT it, and it works as well, if not better. Composition and delegation are more flexible. My current working theory is that inheritance is overrated at best, and too dangerous in the hands of common mortals, because it makes you think on the level of idealized categories rather than concrete things.
As further reading, your explanation of things vs words reminded me of prototypes vs classes, so I'll recommend this article by Henry Lieberman: https://web.media.mit.edu/~lieber/Lieberary/OOP/Delegation/D...
I believe what's missing is not just data. That'd only grant it capabilities that upgrade it to a "record" type of entity. I believe an OOP object is more than a record. It's missing behavior triggered by messages. For an object to pass or receive a message we need to have a model of a message and that requires the notion of a sender and receiver, both objects again. Seems circular, but I'm sure it could be made to work if you properly define everything. Anyway, to my mind perhaps the minimal model of an object is not at all _one object_ but a _relation_ between two or more objects showcasing the minimal "message passing" semantics.
Weird take, but inheritance could be included if you accept something can inherit from itself. A is a type of A, I mean it doesn't strike me as wrong, but it is unconventional.
Or maybe it could give it behavior if it were like that:
This construction implies there are multiple messages. "Multiple" is the key difference. The part that was missing in the original sketch is a second method: Now this is an object. For example, it can be a random number generator: we initialize it and then read next numbers. Or it could be a timer: we initialize it and then read the value. We do not need a fully object-oriented environment for that; there is a plenty of such things in C and other non-OOP systems.Such a thing surely has some behavior and this is exactly what we use. We are not interested in the internal data much. In fact the internal data of an object play exactly the same role as a function stack frame: it is a private slice of memory a computation keeps for itself because this is how it works. As in knitting the size of the manipulator is tiny compared to the final result and to do anything substantial we need a place to keep stuff around until we are done. And we need to be sure data stay were we've put them, hence encapsulation.
So an object is very much like a function, it is a computation that uses some memory to do its job. It is different in that in an object the computation runs step-by-step guided by external events. A function is like an object that gets the whole sequence of events at once, runs from start to finish and in the end discards the working memory so we tend to forget it exists. An object runs from message to message and keeps the working memory, which we see as "object data". This is why objects arise naturally in areas like user interface where events are truly external. But an object is actually a primary form of computation: if you can process a single event, you can use it to process a sequence; but if you can only process the whole sequence, you cannot just switch to processing single events. There are many similarities with closures, coroutines and such; I'd say they are different ways to express the same principle.
(This also means that objects are naturally mutable. Immutable objects are an aberration that arose because we've got here in a very roundabout way.)
side note, when first transistor-based portable radios showed up (as opposed to large lamp-based stationary devices), they were called "transistor". you would take not your portable radio with you, it was your transistor.