This note will attempt to describe how objects will work in BitC. I
suspect that the first and most important thing to say is that BitC will
not have objects.
I am sure that the description which follows will get revised, but it
represents an initial position for discussion, and it will let me state
some of the remaining questions.
So first: what is the point of having objects?
** Code Reuse: No
I disagree with some statements that have been made in earlier
discussion on this list. The real value of objects does NOT lie in code
reuse. Yes, there is code reuse in most object systems, but this reuse
would not seem nearly so remarkable if those languages provided
closures. Essentially all of the reuse that you get out of typical
object systems can be had already if you have closures.
** Subtyping: Rarely
The value of objects also does not lie in implementation inheritance.
Most uses of the is-a-kind-of pattern rely on contravariance and
covariance, which yield an unsound type system. The classic example of
this is the SmallTalk numerics hierarchy, in which you have several
different kinds of numeric types all derived from a common superclass
Number. The only reason this works is that SmallTalk is dynamically
typed.
People attempting to do the same thing in languages like C++ and C#
quickly discover that the *appearance* of a class hierarchy doesn't
really solve anything, because you have to know the static type of the
specific value in order to perform the arithmetic anyway. This means
that a C++ Number class actually needs to know all of the classes that
derive from it anyway!
There *are* valuable uses of implementation inheritance, but these are
much more elusive than they first appear to be.
** Subclassing for Specialization: No
Consider the C++ iostream example. There is a common superclass,
iostream, and many subclasses that exist to supply various
implementations of the iostream interface.
This has come to seem natural to C++ and Java programmers, but it is a
bad approach. What has really happened here is that there was a need to
specify a common interface type, and the virtual method mechanism was
used to provide it because no other mechanism in the language could do
so. This doesn't mean that subclassing was desired!
In short, most of the current uses of subclassing are either (a) bad
idioms, or (b) good idioms overlayed on the wrong mechanism. BitC takes
the position that most languages have erred by confusing interface types
with object implementations. What we want is the ability to define
something like Java interfaces, whose type is a set of ( method-name,
signature ) pairs.
** BitC Interfaces
BitC will have a form DEFINTERFACE:
(definterface stream
get : (fn () char)
put : (fn (char) ())
... )
At present, an interface will consist exclusively of functions. We *may*
augment this in the future to include arbitrary deep-frozen constants.
Interfaces may extend other interfaces:
(definterface suptype
(extends supertype)
... new methods ...)
Note that an interface specification does *not* reveal the type of the
object state in any way, and that a subtype may not alter the signatures
of the methods of its supertype.
In the short term, we will probably implement this using DEFSTRUCT.
This mechanism is type sound. It has the property that all of the good
uses of object subclassing work just fine and all of the bad uses are a
serious pain in the neck.
As with all other type defining forms, DEFINTERFACE may be parameterized
over types:
(definterface (itype 'a 'b)
... method signatures referencing 'a 'b ... )
The major open question on DEFINTERFACE is whether we should provide an
extra level of indirection to reduce space consumption. That is, should
an interface instance be a structure whose members are closed over
state, or should it be a pair of pointers (state, real-method-structure)
[i.e. vtable like] with compiler support? I would be very interested in
opinions on this question.
*** Binary Operators
BitC does *not* provide a mechanism to overload binary operators, but a
Number interface could certainly define a '+' method. Note that the
following combination of definitions is useless:
(definterface Number
+ : (fn (Number) Number))
(definterface Integer
(extends Number)
value : (fn () int))
There is no way within the BitC type system to implement a generic
"plus" operator this way. The closest you can get is to declare
(definterface (Number 'a 'b)
+ : (fn ('a) 'b))
and as you can see, this rapidly becomes a mess. What is being revealed
is that Integers were never a subtype of Number, and that this whole
approach was mistaken. If what you want is extensible binary operators,
a better way to do it would be to write
(define (extensible-plus x:'a y:'b)
(typecase (tuple a b)
... dispatch on legal argument type pairs ...))
Note that there has been a revision to the specification of typecase:
the right hand sides are not required to evaluate to expressions of the
same type.
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev