Ben and I have had a side conversation going about interfaces. Just
recently, a thought occurred to me that significantly reduced my concerns
*if* it actually works.
First, what is the purpose (rationale) of having the Interface notion in
the language? How do they differ from type class instances?
Basically, Interfaces are the mechanism for existential quantification. An
instance of an interface is an existentially encapsulating object. It
consists of a pair: a reference to an underlying state object plus a [table
of] method[s] over that object. The return type and parameter type[s] of
the methods are specified, with the exception that there is a hidden "this"
pointer having existential type that is additionally passed to the
underlying implementation of the method. The BitC "capsule" mechanism is
actually an interface mechanism in disguise.
Some mechanism is needed in the language for existential encapsulation, and
Interfaces seem like a perfectly good way to do it. I certainly shouldn't
rename an established concept, either. :)
By contrast, a type class instance is a compile-time literal constant. It
is a member of a type *relation*, and it can only be used where the target
type is known. No existential encapsulation is enabled by type classes or
type class instances.
In my initial discussion with Ben, I kept getting hung up on the fact that
Interfaces are reference types. This means that every operation that
obtains an interface from an object is an allocating operation. Since
heap-free programming is something we are putting attention on, that is
very unfortunate. It either means that we have to sharply curtail the use
of interfaces in the library (which will quickly break down in practice),
or it means that we have to accept that writing heap-free programs will be
almost impossibly difficult. It even means that certain language constructs
cannot be used in heap-free programs. For example:
foreach i in myCollection
... do something with i ...
might be specified to operate by obtaining an instance of the Iterable
interface. But if obtaining that interface entails heap allocation, the
*foreach* construct is effectively banned in heap-free programming. By
contrast, foreach could be specified to use an Iterable type class with no
danger of allocation and a guarantee of inlining. And oh, oops, that really
seems to work better at first glance, because how can a value type support
an Interface of it has to be pointed at by an object in the heap?
That, in turn, suggested a solution: specify (and enforce) that interface
instances may not outlive the objects that they reference. This commits us
to region checks, but we are committed to those in any case. What will
happen in this case is that most Interface instances which appear at
library call parameters will be typed as non-escaping. This can be
explicitly overridden by suitable choice of region type on the parameter,
but that can back-propagate pretty far through the program, forcing things
into the heap along the way.
The thing is: that won't *usually* happen, because interface objects tend
to have very local lifespans, and interface objects at parameters are
unlikely to escape in normal code. As long as that is true, we won't have
any problem when stack-allocated value types publish interfaces.
Does this idea that instances of interfaces have a lifetime restriction
work? What do people think?
Note that if the underlying type is a reference type, this doesn't really
impose any meaningful restriction on the corresponding interface objects.
When it's challenging to resolve the lifetimes, the compiler will just end
up pushing both the reference object and it's associated interface instance
into.
If the lifetime restriction thing works, then I think we can get away with
interfaces at library parameters, because almost all of those will turn out
to be non-escaping, and we'll end up being able to stack allocate the
interface objects.
shap
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev