Several people on the list have advocated that we introduce some form of
object type into BitC, and I have said that I agree. The compelling use
case was the need for a common stream abstraction.

The question then becomes: what is an object?

For this message, indulge me in ignoring the object/facet distinction. I
want to deal with the issue of first class messages first.

There appear to be two major types of objects: record objects and
variant objects. Record objects are the kind you have in C++ and Java.
Variant objects are the kind you get in SmallTalk. I am not aware of any
strongly typed language that has successfully incorporated variant
objects. Today we learned why, and we made a decision to go with record
objects (at least for now).

There are also the so-called "row types" in ML. These simply are not
objects in the sense that concerns us.


For those (like me) who find that these terms give them no idea what the
difference is, here is an explanation:

In a record object system, methods are invoked on objects. That is,

    ob.method(arg arg)

is a method invocation. By contrast, in a variant object system,
messages are first class. One can write something like:

    (let ((ob (obtype.create))
          (msg (obtype.messagename arg arg))
       ... do stuff ...
       (send ob msg))

in particular, there is a type "messages to object of type ObType", so
you can pass messages through procedures for later delivery:

    (define some-fn 
       (let ((ob (obtype.create))
          (lambda (msg : obtype.messagetype)
            (send ob msg))))

    (let ((msg (obtype.messagename arg arg))
       ... do stuff ...
       (some-fn msg))



** The Type System Problem

It proves that this is an absolute mess in a static type system. The
problem is that the invocation

    (send ob msg)

has no single return type. It's return type is indexed by the method
name. Each method has a straightforward return type, but we need to know
which method is invoked before we know the return type.

Due to recent work by Paritoff Shroff and Scott Smith, it seems that
there *is* a sound type system that can handle this. It isn't simple,
and it has corner cases.

The other option is to introduce a family of message types, such that

  (obtype.messagename arg arg)

returns a procedure of the form

 (lambda (ob : obtype) (send ob (messageselector arg arg))) -> msgResult

This of course eliminates the genericity of first-class messages which
was the whole point of the exercise. It is useful to be able to do this,
but it isn't the full power of variant objects.


** The Implementation Problem:

There is no implementation of (send ob msg) that can return a single
value. It can do so in some cases, but in general it has to return a
pair consisting of the return value and an index into the return type
list giving the type of the return value. This propagates into procedure
arguments, because now any procedure

  (define (f x:'a) body)

must be prepared to deal with

  (f (send ob msg))

with the effect that every polymorphic parameter must potentially (and
with decently high likelihood) be turned into a pair.


** The Decision Criteria

This comes down to an design judgement: is it more important to have
first class messages, or is it more important to have transparency that
when one thing is declared one thing is passed.

For those who would argue delegation, I disagree that this is central.
In a record object system, it is straightforward to introduce syntax so
that a proxy object can declare its backing object, provide a few method
wrappers and delegate the rest.

Given the purposes for which BitC is targeted, I believe that
transparency should be the ruling aesthetic unless there is an
overwhelmingly compelling use case for first-class messages.

Does anybody have one?


shap

_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev

Reply via email to