Wolfgang De Meuter
Last revision: August 7th, 1996

Agora:The story of the simplest MOP in the world


When implementing an interpreter for an object-oriented programming language, the interpreter must have a representation of the objects it manipulates. This representation is an ADT or a class specifying all the operations the interpreter is allowed to perform on its objects. The operations together form the so called 'meta-object-protocol' (MOP) of the objects. It is the protocol of operations the meta-level (i.e. the interpreter) sees when manipulating objects.
In a reflective language, a programmer can write code to alter the interpreter. In order to do this, he/she needs to access objects in the same way the interpreter does. Hence, the design of the MOP becomes very important.

An ordinary MOP often defines several operations on objects. Examples are a message-sending operation (send), a delegation operation (delegate), a cloning operation(clone),etc. Some language even have operations to add slots, delete slots and operations to access the internal state of an object. So, the following MOP gives a very realistic sketch of the MOP's in current day prototype-based languages:

object = {
          private: ...
          public:
            delegate(m:messagename,arguments:objects,self:object);
            addSlot(m:messagename, slot:expression);
            deleteSlot(m:messagename,slot:expression);
            readVariable(m:messagename);
            writeVariable(m:messagename,value:Object);
            send(m:message,arguments:objects)
         }

So when 'going meta', the above operations are the ones a programmer is allowed to perform on objects. The problem is that it is very difficult to trace (perhaps with extensive data-flow analysis) which code is meta and which is not. This is even more difficult in a dynamically typed language. Hence, whenever programmers are not satisfied with the protocol of the objects they get (for example from a library), all they have to do is go meta and breach the encapsulation of the object under consideration. Therefore, the entire idea behind Agora was to design a language in which only a single operation on objects is permitted: the one for sending a message (i.e. send).

object = {
          private: ...
          public:
            send(m:message,arguments:objects)
         }

Notice that once the send message is performed, the object knows all about itself (i.e. the private information above). It can use this knowledge to extend itself or clone itself. This is accomplished by mixin-methods and cloning methods. If the object contains the right implementation for it, it can even decide to export its own implementation details, but this can only be asked to the object by sending it a message. This is at the heart of the safety of Agora.