Reactive programming is all the rage these days, and it brings with it a suite of new abstractions and paradigms. The Scala language provides a fantastic environment for deploying these new reactive techniques, bridging the gap between functional and object-oriented programming and enabling clean interfaces for tools like futures and actors. While the Internet is already full of information and tutorials about these concepts, I thought a simple comparison of some of the core abstractions might help clarify things in a learner’s mind.

We’ll be looking at “traditional” objects, “functional” objects, and actors in terms of three defining properties: state, identity, and behavior.

As our starting point, consider traditional objects, which are often characterized by mutable state, equality by identity, and synchronous behavior via method calls. Languages like Java facilitate this model of objects: member variables are mutable by default, and == always represents the identity operator. Unfortunately, this model of objects has some serious weaknesses in the reactive era: public mutable state presents a host of issues in concurrent environments, solving those issues with locks ruins scalability when method calls are synchronous, and the concept of identity becomes nebulous in the presence of distributed computing.

These properties also make traditional objects incompatible with functional programming, but with a few tweaks, we can define “functional” objects that bring many of the benefits of object-oriented programming to functional contexts. Such objects have immutable state and implement logical equality while still providing behavior via synchronous method calls. As a result, they’re safe to use in concurrent and distributed environments.

But sometimes mutable state and identity are desirable when modeling a particular program component. Is there a way to make them scalable? It turns out there is, by tweaking the “behavior” property; this gives us actors. Actors are characterized by mutable state, equality by identity, and asynchronous behavior via message passing. By making their behavior asynchronous, actors’ states are only modified by a single thread at a time, and their clients do not block when another client’s work is being performed.

So objects and actors are really just different permutations of specifying the properties of state, identity, and behavior. By comparing them in this way, you’ll have a better idea of which is more appropriate for the task at hand. The following table summarizes what we’ve covered:

Property Traditional objects Functional objects Actors
State Mutable state Immutable state Mutable state
Identity Equality by identity Logical equality Equality by global identity
Behavior Synchronous method calls Synchronous method calls Asynchronous message passing