Looking Towards Potoo3 (Part 1: Motivation)
Potoo started out as an experiment to build a JavaScript based actor system, for use in Web Applications
Potoo started out as an experiment to build a JavaScript based actor system, for use in Web Applications.
The motivation at the time was to find a way to better communicate data between the modules of a slightly complicated user interface. That interface needed to be changed rapidly because of uncertain user requirements. The Event emitter pattern was used at first but it proved cumbersome to maintain.
Prior to Potoo, and perhaps before the current trend of reactive programming, we may have handled 1 to N communication between modules either by a Composite pattern or Event emitter/observer pattern.
This leads to a code like the following:
let composite = new CompositeClass([/*various instances sharing the same api*/]);
composite.eventMethodThatWillBeCalledForEachMember(value);
or
let eventSource = new EventSource();
eventSource.on('event', doSomething1);
eventSource.on('event', doSomething2);
eventSource.on('event', doSomething3);
// Somewhere else.
evenSource.emit('event', {});
There are times where both approaches are acceptable, however, both require the enclosing block of code to "know" too much about the source of the events. This creates a kind of coupling that can really get in the way when you need to change things quickly.
Let's say for example, CompositeClass
is deprecated and we need to use CompositeClass2
, then we would need to make changes to that block and anywhere else it is used.
If we decided to use a different EventSource
we would also have to make similar changes. It is relatively easy to miss out one or two places that need to be updated causing subtle bugs where event handlers are not invoked or invoked prematurely. It can also be difficult to create fake implementations of the event sources for testing, depending on how instances are acquired.
Another issue with these approaches, is the sheer cognitive burden involved in keeping track of who is currently a member of the composite or what handler is currently installed on the event source. It's not immediately clear, by looking at the code. Think of the DOM API and how difficult it is to know for sure that your handler is the only listener added via addEventListener.
Message passing appeals here and Potoo was designed with these issues in mind. To some extent, Potoo is probably better understood as a framework for formally passing or better yet, routing messages between objects in JavaScript applications. Formal in the sense that the infrastructure for this is explicit and visible to the programmer rather than coincidental.
The Actor Model allows for those events and handling to be first class citizens in an application and their treatment to be more explicit. This has the effect of making reasoning and maintenance easier.
As time went on, and the design of the framework improved, we found use not only in front-end applications but in server side development as well. It turned out that server side development, despite being request/response oriented, benefits from this mechanism too.
The simplest of modern server-side applications tend to communicate with one or more servers, whether for database services, authentication, payment processing etc.
Being able to treat these facilities as stand-alone actors rather than coding them directly into our main application logic, improves maintenance and makes testing and debugging easier. Our main application is not interested in the details of how these services work, it just sends a message when it needs something done and optionally awaits a response.
Message passing in simple terms is the triggering of some piece of code by another, by simply providing some value, i.e., a message. The triggered code then decides what needs to happen not the code that triggered it. This concept may sound familiar. Indeed we do message passing to some extent when we call a function or method on an object.
A properly designed method or function is not dependent on who calls it or how. It simply accepts its arguments (message) and processes the outcome. There is no need for the caller to know the implementation details.
Having to know less about the implementation details of an algorithm before you use it almost always leads to more maintainable code. This is the concept of abstraction. Less is more as is often said.
Let's say for instance, if we have an interface called `Number` it may have the methods add(n:Number):Number
, subtract(n:Number):Number
and multiply(n:Number):Number
. When we pass a value to the any of these methods, it would be inconvenient to have to indicate whether the number is a float, double, integer, big integer etc. It would be even worse if we had to call some other method before hand like clearPreviousResultSoYouCanGetTheNextOne()
or anything tedious like that.
Instead, we pass a Number
type and some code is triggered, returning the result of the computation. This is basic message passing and output. We do not even need to know if the Number interface is implemented by an Integer, Float or Double class. We just depend on the interface or API of Number
.
Any object wanting to be treated as a Number
simply implements this interface and messages can be sent via its methods. However, what if an object is only interested in providing the add()
service?
The obvious OOP approach may be to refactor the Number interface
to inherit from an Addition
interface that only describes the add()
method. This works, however one can argue that it becomes tedious quickly. As the code base grows, relationships like these between interfaces become harder to grasp as well.
In the actor model, message passing between our objects (actors) is restricted to a single mechanism. Actors do not directly access each other's methods, instead they obtain addresses for them and use the message passing machinery to send a message to that address.
In Potoo, this is implemented by the tell(address, message)
method for resident actors. This method hooks into the internal machinery to deliver a message to the target address. Restricting the API to a single method limits the amount of knowledge actors have about each other.
In this world, it is the message that determines what algorithim is to be executed, not the method calls. In fact, the API of an actor is essentially the various messages it is willing to handle. In this way control is completely in the hands of the receiving actor and is not shared between objects which can lead to coupling.
This restriction, allows for some powerful patterns to be made possible. For example, seeing as actors do not invoke methods on each other directly, we now have an opportunity for location independence. An
actor we are interested in may reside in the memory of the same process, it may be part of another process, or even on another machine!
Regardless, message delivery passes through the same mechanism so we don't have to change our sending code if our receiving object changes. For example, if we have an actor in our code base encrypting text, that slows down the main program, we could easily place it on another process or worker thread without much fuss.
This is the potential we aim to unlock with Potoo 3, in this brave new world where third party services are the norm in the simplest of applications.
Part two coming soon.