Instances

BOOL Instance objects are the data objects of BOOL. Each Instance object is an instance of some Model. There is a small number of Native Models supporting basic data types (integer, floating point, string, and others). Users can define new Models to support new data types.

Description

In BOOL, data is actually split into three parts: a static code object with an address, a run-time data object that’s a handle to the data value object. The code object is “location” (so to speak) and is the only part that appears at the source level. The run-time data object lives in the stack and call frame, plus it accounts for any structure of composite (and list) types. The value object is managed by the associated Model and accessed via a data key (dkey).

[Some analysis needs to be done regarding garbage cleanup and synchronization between data objects and their value objects. If data objects proliferate so that multiple objects refer to a single value object, some way to detect when the last one is destroyed is required. (Reference counting being an obvious choice here.) There is also the matter of telling dynamic objects from static ones when destroying call frames.]

A code object has a link to its Model, an index to a slot in the call frame and a link to an initializer list (or a null link if the instance has none). The format is fixed. The call frame index is the next available at that point in the code block.

Syntax

*model-name instance-name initializer-list

The model-name must be an existing Model known to the compiler. An alternate syntax replaces model-name with (object-name) where object-name specifies some known object — the parentheses are required. In this case, the Model is taken as from the referenced object.

The instance-name is the name of the instance. It follows the usual rules for BOOL names (specifically, such names may contain hyphens, slashes and various other symbols usually not allowed in object names).

The initializer-list is optional. The rule is, if a List object follows an Instance object, that List is an initializer for the Instance. (Which means you need to be aware of the potential for a following List to bind unintentionally. Fortunately such cases should be rare to the point of not existing.)

Examples:

*int i1 = 0
*int i2 = i1
*string s1
*point p1 = 0,0

The exclamation point symbol refers to the “self” or “this” object. The @move Action is bound to the move: message because of the double-colon after the name. Without it, the Action is private to the Model.

Implementation

Instances have the richest set of behaviors, so they respond to many messages. But the Instance object just hands itself and the message over to its Model to handle.

This ultimately results in the Model invoking a Model Action that presumably pushes some result to the stack. That result is really the result of applying the message to the Instance, so it is left to whoever sent that message to the Instance to consume the object on the stack.

Certain System Messages don’t invoke the Model and dispatch the request at a low-level (the W: message, for example, can be handled by the SendMessage dispatcher).

The X: message represents the thread of execution and is a request for the instance to initialize itself. It’s at this point that any initializer list is queried for value(s). Initializing the object may, or may not, require the Model’s involvement.

The Q: message is a request to push the Instance’s address onto the stack.

The M: message is a request to push the Instance’s Model’s address onto the stack.

Python binding

There are actually three kinds of objects that appear in different places. The first two are the static and dynamic code objects that provide an address for the third kind, the data object. The static form appears where source code declares an Instance. The dynamic form appears with Message and Actor objects to provide an address for their result objects. Data objects exist in call frame slots and the stack.

The model field links to the Model object that this Instance object is an instance of.

In code objects, the dx value is an index into the call frame of the enclosing Action. The physical difference between the static and dynamic types is that static types include an initializer-list. If this field is non-null, it links to a list of values used to initialize the Instance. (Given that most of them are scalars, the list usually has just one value.)

Code objects can have optional properties, each of which is a name-value pair. A few key ones are: name, size, bits, signed. The first, name, is helpful for debugging; the rest constrain the Instance. Properties may be listed in any order.

In data objects, the dkey is a token that accesses the data value managed by the Instance’s Model. The data object is a handle to that value, and there can be multiple such handles referring to the same data. The actual data, as such, is managed by the Model.

Data objects also implement any structure a composite instance has. For example, a declared Instance of the two-member *point Model might look like this in the code:

(INST, 'point', dx, null)

And like this in the call frame or stack:

(OBJ, 'point', [(OBJ, 'int', dkey1), (OBJ, 'int', dkey2)])

Which is not to say a complex Native or Binary Model would need the structure of its objects expressed that way. Such Models are allowed (encouraged) to maintain complex data structures if they desire. (But this is for implemented Models. The required capability isn’t exposed at the source code level… for now.)

If *linklist is a Native Model (which is strongly suggested!), its code and data objects would look very much like those for any scalar data object:

(INST, 'linklist', dx, null)
(OBJ, 'linklist', dkey)

But in this case dkey accesses a linked-list data structure. (Including any nodes. The nodes would probably just have links to external data, but could contain their own payload, depending on the design. As such, the managed part of the data could be large!)

Arrays also express their structure in the data object. If you declare the following array Instance (of ten *int Instance objects):

*array <*int> [10] A

The code object looks much like any other, except for the addition of two properties storing the array type and size info:

(INST, 'array', dx, null, ('type','int'),('size',10))

The data object contains all the array members:

(OBJ, 'array', [(OBJ, 'int', dkey),...nine more...])

One observation this leads to is that arrays and user-defined Models don’t manage their own data space. (In some cases, arrays might take over managing an array of binary objects.) As mentioned above, the tools for managing a Model’s data space aren’t available at the source code level, so user-defined Models can’t implement data management.

This may also lead to the observation that large arrays in BOOL can take up a lot of space — that they aren’t very efficient. This observation is correct. But then BOOL was never intended to be efficient!