However, on the object oriented model track, due to practical reasons, its definition descended into a single-thread dispatch system with full message delivery guarantees. Locality was dropped (due to singletons) and the distributed model was not maintained. Method dispatch systems were later added, but could be considered a kludge. This also explains the mismatch between remote systems calls (SOAP, REST, etc.) and the internal language. Ideally, these would be the same.
With our modern systems design constraints, especially given distributed systems, we need to revisit those early decisions. The Actor model is a good blueprint for our designs. It is fundamentally decentralized, locality is enforced and at-most-once message delivery is assumed. These allow us to design and implement distributed algorithms which would be hard to implement using traditional OOP methods.
An Active Object is essentially an OO encapsulation of a worker thread. It provides clients with a synchronous call interface where all results are resolved asynchronously (e.g. using futures for results). Internally, the Active Object converts client requests into asynchronous operations, enqueues the operations, and executes them on a private worker thread using object-specific dispatcher logic.
In an OO system, not all objects are required to be Active Objects. So there may be a mixture of synchronous and asynchronous execution.
In contrast, in an Actor system all communication is via asynchronous messages and there is no inherent requirement for multiple concurrent threads of execution.
Active Objects use a mechanism such as futures to return results. On the other hand, if there is a request-reply message exchange in an Actor system, both the request and the reply will be separate messages. The client would receive the result of a computation by receiving a message.
The relationship I see between Actors and Active Objects is that both require some kind of queue for dispatching operations (since Actor behaviors are never re-entrant). Active Objects convert synchronous calls into asynchronous operation requests, whereas Actors use asynchronous messages for _all_ inter-entity communication.
I agree with the GP that event loop programming is the closer OO analogue of Actors.
A good place to start is perhaps learning Elixir and eventually move to OTP and it's actor model.
Actor are just processes in Erlang/Elixir (underlying implementation is a thread that doesn't share anything) that isolate logic (self contained) and if it goes down it doesn't take any thing else down. So it's OO that way. It helps that functional paradigm good practices is small compose-able function that does one thing. You compose these functions into a logic and wrap it in these processes and treat them like objects.
PLANNER was the precursor to Prolog. Erlang a language based on actor model where processes are actor was originally not intended to be a language, rather it started out as a library for fault-tolerant distributed programming in Prolog, and later evolved into a dialect of Prolog, before it became its own language, still to this day heavily influenced by Prolog.
So, the similarities between Objects in OO, and Actors in the Actor Model are far from coincidental.
Object oriented programming is a language feature while the Actor paradigm can be build on top of OOP to ease the mental burden of parallel programming.
I guess you mean 'concurrent programming', right? I haven't seen actors used a lot for parallelism.
So a pool of worker actors can absolutely work in parallel. In fact now I'm curious if Actix supports that
For pure parallel computing it introduces unnecessary overhead because of the message passing. That overhead in turn hurts performance, which really is the only reason you'd want to compute in parallel.
It's common to conflate parallel programming with so-called embarrassingly parallel tasks, but this isn't accurate. For tasks which may be executed in parallel but aren't working on different regions of a single state, actors are an excellent choice.
Parallel computing is simply running more than one aspect of a computation simultaneously, it requires multiple processors/cores, or SIMD. Concurrency is doing more than one thing 'at a time', and this generally includes things like callbacks (to allow more work to be done while waiting on an action) and preemptive threading (which may or may not involve true parallelism).
Concurrency is an opportunity to work in parallel, one which may or may not be achievable. I consider threads a bad concurrency primitive, because they're too low-level and hard to get right, and this becomes even worse when one runs threads in parallel.
Actors, which are a share-nothing concurrency model based on message passing, are a good concurrency primitive. Among the reasons for this are that one can put them on threads and not have to deal with locking and unexpected mutation. You can treat them as an implementation detail that happens below the level the programmer must concern themself with.
This means they're good for running in parallel, as well, which is quite tractable on a single machine given that it has multiple cores (mine has eight).
Colloquially we sometimes say 'parallel computing' when referring specifically to so-called 'embarrassingly parallel' tasks, like some rendering algorithms, where one may bring as many cores to bear on the task as one has available.
But concurrency is always an opportunity for parallelism, and an actor model allows one to take that opportunity given that other aspects of the runtime don't stand in the way. And parallel computing is simply running more than one computation at the same time, it doesn't by itself imply anything else about the algorithm.
Inter-process and inter-machine parallelism do that anyway.
Will changing the name fix that problem? I think any language or framework that gains widespread adoption will have to compromise its principles in some ways for the sake of pragmatism.
What I'm asking is, how will the actor model stop people from taking those pure ideas and turning them into another Java? Isn't it just a matter of time? If that's the case, then it hasn't really "fixed" anything about OO.
It doesn't guarantee parallelization. Concurrency is a precursor and a requirement for parallelization but it doesn't guarantee it.
There are also some points with the messaging being truly asynchronous, so you can't rely on the order in which messages arrive, usually subject to certain constraints. Erlang, for instance, require that between any two pairs of processes, order is preserved in the messages.
Also, creating a new actor creates a new thread of execution inside that actor. This is not the same as creating a new object.
UNIX shell with isolated processes communicating over streams -> Actor model
Pretty much every C-style language with all of their caveats -> Object-oriented model
That sounds like COM, especially the Automation subset.
Go doesn't come close; technically it's just another shared-state programming language. Culturally, it tends to use more sane concurrency features, and the channels are nice and all, but technically there is no isolation between goroutines.
With discipline, you can program Go with an actor mentality and it's fairly effective. I do it all the time, leaning on my years of experience in Erlang and some Haskell, which teaches you how to build systems that work that way. But you do need non-trivial discipline as the language provides rather less help with than I'd like.
The restrictions you talk about are the key advantage of the actor model. OOP is extremely powerful, and nobody seemingly knows how to use that power responsibly. Actors introduce just enough rails to drive you into the pit of success.
Just like Rust imposes restrictions to improve the reliability and security of software, Actors impose restrictions that improve the human comprehension and reasoning of software architecture.
Most people who talk about OO, are talking about the Java style.
Actor is the association of object with an execution thread.
So, actor = bounded (functions , state , thread) (thread in the sense of execution thread, not necesseraly an OS thread).
With this definition, actors can't "share" a thread, and so need to communicate through asynchronous message passing / function invocation.
Method calls are synchronous and they are not allowed to fail under any circumstance. But in the context of distributed systems you have to choose between at most once or at least once delivery which are semantically incompatible with OOP style method calls. Your methods will have to be written with the expectation that they are called multiple times or maybe not at all and most OOP code doesn't satisfy these expectations because the OOP model doesn't require it. If you extend the OOP model with message semantics of the actor model then what you get is not just OOP+messaging. It's the actor model.
Also you may have misunderstood something very fundamental. The benefit of paradigms isn't to be completely different. It's that everyone that shares a paradigm follows the same design philosophy. Imagine a mixed code base consisting of traditional OOP (with locks), actor model, async/await, promises. It would be very difficult to understand and sometimes it isn't obvious which of these is used. Some functions are asynchronous or they may fail but you may not know that because every single line could do a different thing. They may even be incompatible with each other. If you follow a single paradigm instead of many different ones you can avoid a lot of confusion and wasted work.
Someone more clever than wise may stand up and point out that technically even a local function call is unreliable in many of the same ways that a network call is. However, even if many of the same problems can theoretically arise, the distribution of the problems are fundamentally different, which is why you do not guard every single function call in your program for all the network-type errors, and it generally works, whereas if you program that way in a networked environment, it generally does not work.
Personally I've come to prefer messages very strongly to "RPC" as the foundation of a system. Messages can trivially implement an RPC-like system, but RPCs can not implement a message-like system. Even if you implement an RPC called "SendMessage", you're still adding synchronization on the RPC system sending over the call and waiting for the response. Among other things, but that's the big one.
That's the reason why remote calls today are using explicit syntax, API and so on.
But with actors it's expected that sending a message to actor is something that takes some time. So you have a clear separation between ordinary function calls and actor messages.
Of course it's still a lot of difference between sending message to a thread or sending message to another machine. So take that with a grain of salt. But at least you can utilize multicore CPUs.