Skip to content
Sébastien Doeraene edited this page Aug 10, 2012 · 8 revisions

Using interfaces

In order to abstract away actual data types, the object model supports interfaces. An interface defines a set of methods that are more or less semantically related.

Contrary to interfaces in most programming languages, which are implemented only by certain data types, interfaces in the VM object model are conceptually implemented by all data types. It's just that interface methods have a default implementation (for data types that do not provide a specific one). For most operations, this default implementation raises a type error exception.

In a previous page, we used the following trivial code:

UnstableNode node = SmallInt::build(vm, 5);
RichNode richNode = node;
assert(richNode.is<SmallInt>());
cout << richNode.as<SmallInt>().value() << endl;

Let us now update this code to use the IntegerValue interface:

UnstableNode node = SmallInt::build(vm, 5);

nativeint value = 0;
OpResult result = IntegerValue(node).intValue(vm, &value);

if (!result.isProceed()) {
  // wait requested, or exception thrown, see later
  // but usually this information must fall through and reach the caller
  return result;
}

cout << value << endl;

// Everything went well
return OpResult::proceed();

Now, we have abstracted away our code from the actual data type which was SmallInt. The method IntegerValue::intValue() takes care of the dispatching for us.

However, now this can fail, either because the node referred to an unbound variable, or because it referred to a boolean or any other data type. In these cases, the Oz semantics require that we respectively wait until the variable is bound, or raise an exception. This is encoded into the OpResult value that is returned by intValue(). If result.isProceed() is true, that means that the call executed normally, and we can proceed to displaying the value. Otherwise, we need to forward the information to the caller. Eventually it will reach the emulator loop, which knows what to do with such things.

This pattern is used so often that we introduce a macro MOZART_CHECK_OPRESULT that encodes it. Using this macro, the previous code reads as follows.

UnstableNode node = SmallInt::build(vm, 5);

nativeint value = 0;
MOZART_CHECK_OPRESULT(IntegerValue(node).intValue(vm, &value));

cout << value << endl;

// Everything went well
return OpResult::proceed();

which we believe is much more readable.