Readers are assumed to have at least a passing familiarity with an object-oriented language and an actor-oriented language (e.g. Erlang, Pony). Note that objects and actors are orthogonal abstractions and therefore a language may implement both – Pony is an example. Language abstractions orthogonal to both objects and actors will not be considered here, e.g. static vs. dynamic typing. Because Pony mixes objects and actors, this paper will use Erlang as the prototype for actors in order to draw a sharper contrast between objects and actors. Further exposition of Pony will require its own paper. Fundamentally, software is logic interacting with state. Logic is encapsulated in functions (aka methods) and state is visible to one or more functions. When state is visible to multiple simultaneously active functions, and one or more of those functions can write that state (aka shared mutable state), accesses must be coordinated in order to guarantee consistent behavior. Both objects and actors are encapsulation abstractions which define a boundary around logic and state. Figure 1 depicts the structure of a typical object and a typical actor. An object encapsulates one or more public methods, zero or more private methods, and internal state. Each method encapsulates logic and local state. All methods may call each other and the public methods of other objects to which they have a reference. The internal state is visible to all methods, e.g. instance variables. An actor encapsulates one or more functions and a message queue. Each function encapsulates logic and local state and all functions may call each other. All functions may receive messages from the queue and send messages to other actors. For a given problem domain, the logic and state in an object implementation and an actor implementation will be substantially the same – modulo the abstractions made available by the language. The most salient differences between objects and actors are the entry points across their encapsulation boundary and the sharing of state within this boundary. As depicted in Figure 1, every public method of an object is an entry point; actors have only a single entry point in the form of a message queue. Within an object there is mutable state visible to multiple functions – thus each individual object is potentially an instance of the shared mutable state problem in microcosm. Within an actor there is no mutable state visible to multiple functions – the message queue is visible to multiple functions but it is read only. Given that public methods of an object can be called at any time in any order by any external method with a reference to that object, these public methods can be simultaneously active even within a single thread. Therefore the object’s internal state can change while a method is executing even if that method did not change said state. A simple example of this scenario is depicted in Figure 2. In general, the shared mutable state problem may occur when two or more instances of methods within the same object are simultaneously live, i.e. have frames on the stack. Note that these could be instances of the same method. In order to guarantee this behavior does not occur, access coordination logic will need to be implemented around the internal state of the object even for single threaded implementations, e.g. locking. This definition encompasses recursion but in that situation state modifications are contained within the same function and are immediately apparent to the developer. Messages can be sent to an actor at any time in any order [1] by any actor with a reference to that actor. Messages are processed serially by any function within the actor; logic within the function determines the point at which messages are processed and which specific message in the queue to process. Because messages are processed serially, the corresponding logic is executed serially. Thus the actor model does not exhibit the shared mutable state problem inherent in the object model. Even if an object were to be designed to have only a single public method to mimic the single entry point of actors, that public method can still be called at any time in any order by any external method with a reference to that object and therefore the shared mutable state problem is still present. For objects, methods external to the object determine when, and in what order, logic within that object is activated. For actors, logic internal to the actor itself determines when it will be activated (by extracting a message from the queue). Thus, fully comprehending the behavior of an object requires developers to look beyond the object itself – potentially far beyond. Fully comprehending the behavior of an actor requires inspection of only the functions within that actor. Given that logic within the actor decides when to process the next message and which message to process, it would be simple to implement logic to guarantee that invariants are maintained before each message is processed. Implementing a similar guarantee for objects is more complicated. Because the actor model does not exhibit the shared mutable state problem, an actor implementation that executes correctly in a serial scenario will execute correctly in a concurrent scenario without modification. The same cannot be said for an object implementation. To gain experience with the actor model, I suggest starting with a language for which the actor model is intrinsic. The actor language with the largest ecosystem and market footprint is Erlang. Erlang programs are compiled to a virtual machine that schedules actors. Pony is a promising new actor language that blends actors, objects, and capabilities. Pony is a compiled language and an actor scheduler is linked into the binaries. When transitioning from an object model with threaded concurrency to an actor model with message passing concurrency, developers will need to make a mental realignment. Actor creation is almost as fast as a function call and this changes the optimal approach to scaling. Imagine a system that processes messages requiring a sequence of steps. Rather than having permanently running actors for each step where each actor serially executes a step in the message processing sequence, a better approach is to spawn a set of transient actors for each message when it arrives. The bottom line: When compared to the actor model, the object model is more brittle, its encapsulation more permeable, and its cognitive load on developers higher. To summarize, the advantages of actors relative to objects are: No shared mutable state problem in serial or concurrent scenarios Zero modification needed to go from serial to concurrent scenarios Easier implementation of invariant enforcement between logic activations Lower cognitive load on the developer [1] Erlang and Pony guarantee that messages sent by actor A to actor B appear in B’s message queue in the order A sent them.