In E, references are distinct from the objects they designate. This might seem apparent, but it is not necessarily so. In traditional languages like Java, first-class references are almost indistinguishable from the objects they designate. They are internally represented as 4- to 8-byte pointers and while there is a distinction between reference equality (two references pointing to the same object) and object equality (two distinct objects with identical/indistinguishable contents and behavior), there is not much else to worry about.
In E, however, the rabbit hole goes deeper. There are multiple *types* of references. The word type might be a misnomer but I find it as one good way to think about references. In the E thesis, there is no discussion of types of references, but rather states that a reference can be in:
- Local Promise
- Remote Promise
- Near Reference
- Far Reference
- Broken Reference
In this discussion reference type is a synonym for reference state. The reason the term state seems more appropriate is that a single reference goes through several transitions between states in the course of its lifetime. In other words, a reference can switch types.
The problem this poses for an implementation is that references in different states hold different information. A near reference is the simplest case, the familiar reference from Java – it holds the address of an object within the current VM’s heap. A promise however, holds an unbounded list of pending messages and whenResolved listeners. A far reference holds whatever information is necessary to transmit messages to its target object, including potentially a distinct queue of messages pending delivery. A broken reference holds exception information regarding the reason for the reference breakage.
Classes are a natural way to think about implementing each different state of a promise – the information and behavior for each state of a reference is represented by a distinct class. The problem arises when the reference needs to switch states, therefore the need arises for an object to change its class dynamically, which is not traditionally available functionality in object-oriented programming languages.
Another problem is the possibility that references might chain. In other words, the possibility that a reference might point to another reference, instead of directly pointing to an object. A promise might get resolved to another promise. Or, even more disturbingly, a far reference might point to a promise reference. This possibility of chaining is actually excluded in the reference states model presented by Miller. Instead of a promise resolving to another promise, the promise reference simply makes a state transition, or in other words, the reference becomes the other promise, instead of pointing to it. In a similar fashion, a promise will get deserialized as a promise for the same result as the original promise, instead of being deserialized as a far reference to a promise (which would introduce chaining of references).
This, in essence, leads to an important conclusion. The serialization/deserialization implementation must include special handling logic for references in different states. For instance:
- Near reference might have to be deserialized as a far reference
- Near promise is deserialized as a remote promise linked to the same resolver as the original near promise
Furthermore, the resolution logic must handle the distinct cases as well, in order to handle the different state transitions possible that originate from the promise states:
- Become another promise (for a new result)
- Resolve to and become a far or near reference
As already discussed, the most natural way to implement the different reference states is as classes. At this point the notion of reference states as types comes into the picture. References are objects in our runtime VM but they are a distinct type of proxy objects that provide special services and require distinct treatment from regular application objects. As explained above, for deserialization and resolution purposes, we need to be able to distinguish between a near reference to an application object and a near reference to a reference object (since fundamentally the runtime VM only provides primitive support for near references, and the other reference states are reified as regular objects). Furthermore, it is clear from the examples above that we also need to be able to distinguish between the different *types* of reference states (Local Promise vs Remote Promise vs Far Reference etc).
Since in Newspeak, there is no global namespace for classes, and at runtime all classes are simply a dynamic aggregation of mixin applications, we cannot test the class names of objects. Conceptually this would be equivalent to having some sort of type system anyway. But this is exactly what we need – to be able to distinguish between different types of objects (one per reference state), albeit a very restricted set.
Since the Past and Actors libraries are a core part of the language, I propose to meet the need for type checking using the following idiom, which is slightly different from the is* message idiom for arbitrary objects, already implemented in the NewseakObject doesNotUnderstand: protocol. The idea is that since the Past and Actors libraries are singleton modules and the sole managers of instances of objects that represent reference states, and not likely candidates for extension by applications, we can simply test for class equality like this:
(obj class = Promise)
where obj is an object whose type is being tested and Promise is a reference to the class instance local to the current singleton module instance. Naturally,
is exclusively used to construct Promises from within the Past module, for example.