Binding Instances and Models

There is a design choice involved with regard to how Instance objects are bound to their Model object. A similar choice exists concerning Actor objects and their Action objects. (Actors can be called “instances” of an Action.)

The choice is between a BOOL-like design where the Instance sends a message to the Model, or an implementation design that lets the Instance directly call the Model. This post documents the two choices and the resolution to use the latter.

It’s sometimes confusing keeping implementation separate from “the BOOL approach” (which uses a collection of meta object instances that pass messages to each other). From the beginning, one goal was that implementation details should be as BOOL-ish as possible.

BOOL objects should be internally treated as BOOL objects as much as possible including using the SendMessage() API to accomplish as much as possible. As such it’s possible to sometimes lose sight of when that’s not practical or useful.

This may not actually be one of those border cases — the matter seems clear enough once thought out — but I find myself dithering a bit over the choice. Now that I’m building an implementation, things need to be locked down.

This means the Model meta class requires a Dispatch() method, and the Action meta class needs an Invoke() method. Sub-classes that implement native Models and Actions also require the respective methods.

Importantly, neither the Model nor the Action use their HandleMessage() methods to process requests from their “flock” of instances. That method, as usual, only processes messages sent directly to the object.

The downside is the idea that Actor objects know about the Action’s Invoke() method or that Instance objects know about the Model’s Dispatch() method. That breaks the purity of the SendMessage() API. On the other hand, very early versions of BOOL (way back in the early 1990s) featured a direct link binding the flock objects to their shepherd object.

Expressed as a Python tuple, an Instance object might look like this:

(INST, T_int, dx, init-list)

Where INST is the address of the Instance meta object and T_int is the address of the Model object that implements the *int Model. The BOOL engine simply send messages to the objects at those addresses.

At that point, the architecture involving meta objects didn’t exist — each object did what it did in response to messages without depending on common meta machinery. The binding in the current architecture is dynamic and occurs at run-time. This requires common meta machinery driving the various meta-type’s behaviors.

There is also the matter of native objects versus code-defined objects. The former involves run-time instances of meta-sub-classes derived from the Model and Action meta-classes. The latter depends only on the Model and Action meta-class instances themselves. At some point, the run-time engine needs to branch between the two.

Finally, a using the message-passing API requires pushing the calling object onto the stack as a parameter. In the case of an Instance object calling a Model object, the message also needs to be passed on the stack.

Plus, the HandleMessage() API needs to differentiate between a message intended for the Model or Action meta object and a message from a calling instance. The idea for this is that Model and Action meta objects simply wouldn’t respond to messages in the normal fashion (and there was no real reason they needed to other than reflection). They could assume that any message was from a calling instance. In particular, the Model meta object could assume the received message was the Instance object’s received message.

That last was a horrible kludge, but it got around having to push the message. There was no way around having to push the calling Instance or Actor.

So, bottom line, the Instance meta knows about the Model meta, and the Actor meta knows about the Action meta. The Model and Action meta classes support direct access by their client objects. And the Model and Action meta objects do the run-time binding, including branching between native and defined objects.

Ultimately, it’s helpful to remember that message-passing is primarily to bind expressions and get values from objects. Using it ‘under the hood’ is not a requirement, and in some cases is a distraction. This seems to be one of those cases.