-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

On Reflection in Object Oriented Languages

Presenter: Therapon Skotiniotis
Scribe: Viera K. Proulx

The papers

D.A.Moon, Flavors: Object-oriented programming with flavors, OOPSLA 1986
D. G. Bobrow, et. al., CommonLoops: merging Lisp and object-oriented programming, OOPSLA, 1986
P. Maes, Concepts and experiments in computational reflection, SIGPLAN Notices 22(12), Dec 1987
P. Conte, Metaclasses are first class: ObjVlisp Model, SIGPLAN Notices, 22(12), Dec 1987
J.-P. Briot and P. Conte, Programming with explicit metaclasses in Smalltalk-80, SIGPLAN Notices, 24(10), Oct 1989
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

Moon on Flavors

The first paper, by Moon, presents Flavors - a functional language with objects. It is apparently the first time objects have been introduced into a functional language. A flavor is a language feature which represents the structural characteristics of an object. We may define a flavor as follows:

(defflavor ship (x-velocity, y-velocity, mass)
                ()
                : readable-instance-variable
                : writeable instance variable
                : instantiable-instance-variable (constructor))

- messages are used to communicate between objects
- generic functions come into play

For example, there may be a generic method speed.
We then define the method speed for the flavor Ship as

    (defmethod (speed Ship) ()
       (: code1) )

If we now define a new flavor which is a subclass of Ship:

    (defflavor plane() (Ship))

we can change the definition of the method speed:

    (defmethod (speed plane) ()
        (: code2))

How do we determine which method should be called?

- in the method invocation (speed p1) - we first find the p1, then look for the most specific method available

Multiple inheritance is permitted in Flavors, so we can define

    (defflavor plane() (Ship, cow, dog))

What happens if in multiple inheritance there are several methods or instance variables that apply?

For example, in

    (speed p1 a b c)

- how do we deal with ambiguity?

There needs to be a protocol to resolve multiplicity. Theo presented several such protocols:

One option is that one of the choices is designated to be the default, and it is possible to change the default choice at a later time.

Another possibility is to define a method combinator to perform the method in turn on all arguments. For example:

    (define-method-combinator isum +)

Other possible protocol is do-first, and there are other posibilities.

At this point there was a short discussion on what should be in the environment, with Matthias making a categorical statement (possibly attributed to someone, but I did not catch that) to the effect that if there is something in the environment that we want to change, then it should be in the language. An example that was given was any IDE that asks people to build a 'project' :)

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

Bobrow/Gonzales paper on CommonLoops (1986)

The first premise is that everything is an object.

The key interesting thing in this paper is how it deals with multimethods.

The following picture explains the dispatch:

         discriminating function
      +---------------------------+
      |                           |
      |   code --------------------------> for meta combinator
      |   ----                    |        (a dispatcher)
      +---------------------------+
        |
        |   discriminating object - holds the function name and all
        v                                 implementations
      +---------------------------+    
      |     MOVE                  |
      +---------------------------+
      |  ( .  .  .  .  )          |
      +----|--|-------------------+
           |  |
           |  +------------------------------+
 +---------+    the function implementation  |
 |    +-------------+     that will be       |
 |    |     MOVE    |    actually called     |  +-------------+ 
 |    +-------------+                        |  |     MOVE    | 
 |    |  block     ---> type specifiers        |  +-------------+ 
 |    +-------------+                        |  |  block     ---> type specifiers 
 +--->|   code      |                        |  +-------------+ 
      +-------------+                        +->|   code      |
                                                +-------------+   
 

The discriminating function has the code for the meta-combinator that determines which function (or functions) are to be evaluated for a given invocation. The discriminating object holds the function name and has a link to each implementation.

This implementation is the code which will be executed when the method is invoked.

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

Maes, 1986, on 3-KRS

Maes presents a language 3-KRS, an object-oriented language with reflection.

We recall what is needed for reflection:

For object-oriented language to be reflective it needs to have a reflective architecture:

  1. the interpreter gives you access to the structures that represent the embedded account

  2. the interpreter provides the causal connection

There are two rules:

One can represent the computation via the following picture:

                                  Computation
                                /            \
                              /                \
                            /                    \
                          /                        \
                        /                            \
                      /                                \
                "object"                             reflective
 (meaning what used to be called 'object code')      ask, and get answers
 ask, and get the answer for some Domain                  + change "object"
                                  ------

The evaluation and the reflection proceed as folllows:

Originally, a message object refers to its message-meta-object through a 'meta' link and this meta object has a 'referent' link to the message object. A message-meta-object has a link to the evaluation of the message.

To introduce reflection to the method evaluation, we make message refer to a new message-meta-trace object, whose referent points back to the original massage, but whose evaluation link points to the code that wraps the message evaluation in 'before' and 'after' segments.

The type of the message=meta-object points back to the message=meta-object:

                 < MESSAGE-META >  ------------------> eval-message
                    ^      |    ^                           ^
                 M1 |   R1 |    |                           |
                    |      v     \                          |
                  < MESSAGE >     \                        /
                    |      ^       |                      /
                 M2 |   R2 |      /                      /
                    v      |    /           / before    /
             < MESSAGE-META-TRACE > ------> |  eval --
                                            \ after

    M1, M2 : META      -- M1 is replaced by M2
    R1, R2 : REFERENT  -- R1 is replaced by R2

So, the embedded account of the object is encoded in its meta-object.

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

A little bit of history

The idea of "Meta class" was first used in Smalltalk, but that design was limited as shown below. The question is ('chicken or the egg') - what is the meta class of Class (the base for all classes)? Everybody inherits from the base MetaClass, but it is a class as well and must have its meta-class. To set up the hierarchy correctly, it is designed as follows:

                          MetaClassClass                      
                           |   ^        ^         
                           |   |          \       
                           |   |            \     
                           v   |               \  
                          MetaClass              \     
                          /      \                 \ everybody inherits from MetaClass 
                        /          \                   - but it is a class itself...
                      /              \
                    /                  \
  MI:          Point Class       Object Class 
inheritance       |
meta-class        |
                  |
                  |
  C:            Point
 class            |
                  |
                  |
 instance         p

However, MetaClass extends MetaClassClass and MetaClassClass extends MetaClass - a circular dependency.

CommonLoops needed a similar structure. It could have 'infinite tower' - but had no access to it.

It had Classes but not Objects.

Meta class - is something that can create classes - but provides no reflection and therefore no causal connection.

In the example above, PointClass is a meta-class of the concrete class Point, and similarly, ObjectClass is a meta-class for a concrete class Object (not shown). But the only thing the meta-class can do with regard to reflection is to define/change the constructor for the concrete class Point (or Object).

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

ObjectVlisp (Cointe 1987)

Smalltalk '76 - added what was 'missing'
now we are adding it to Lisp

There are 5 things that describe ObjectVlisp

  1. everything is an object < data, procedures>

  2. protocol (send obj selector args1, ..., argsn)

  3. every object belongs to a class

  4. a class is also an object - can be instantiated by other classes

  5. a class is also an object
    class can be defined as a subclass of one (or more) class(es)

The circular class hierarchy looks like this:

                        Object
                          |
                          |
                          ^
        +---------------/---\---------------+
        |                                   |
        |                                   |
      Class                                Point

but:

Point is an instance of Class
Object is an instance of Class
-- and Class is an instance of Class

Note: MetaClassClass and MetaClass from the earlier example from Smalltalk are here collapsed into Class - hence the circular self-reference.

Bootstrapping: how do you make a Class that is an instance of Class?
You set! 'stuff' at the start, then 'kick the chair from under yourself.

Class encapsulates the 'stuff' of the meta-class.

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

Smalltalk '80 (Broit and Cointe 1989)

The paper presents Smalltalk '80 with the implementation of ObjectVlisp = ClassTalk

- eventually, they derive a library of meta-classes.

The focus in on implementation, but it provides examples of use.

Here is an example that shows how the multiple inheritance is leveraged to implement a type stack:

                 Multiple Inheritance Class
                             ^
                             |
         Abstract Class      |     Typed Class
                     ^       |         ^
                      \      |        /
                       \     |       /
                       Abstract Type
                              ^
                              |
                              |
                              |
        Stack <|------- Type Stack
                            ^  ^
                           /    \
                         /       \
                       /           \
              Integer Stack      String Stack