Selftype: an idea from Scala for D2.0
David B. Held
dheld at codelogicconsulting.com
Fri May 25 21:21:48 PDT 2007
eao197 wrote:
> On Fri, 25 May 2007 21:48:10 +0400, Henning Hasemann <hhasemann at web.de>
> wrote:
> [...]
> class Demo {
> type A // This member must be defined in a derived class.
> // There isn't any restruction to type A.
>
> type B <: Another // This is another member which must be
> // defined in a derived class.
>
> /* ... In the body of class Demo names A and B can be
> used as template parameters in D templates ... */
> }
Your use case is a form of metatyping. In the Subject/Observer example,
you notice that the types S and O may be template parameters, but you
want to restrict their type to be subtypes of the nested
Subject/Observer types. That is, you want to provide metatypes for the
S and O metaparameters.
> This looks similar to your proposal:
>
> interface ContainerIterator(T) {
> T content();
> }
>
> interface Container(T) {
> type Iterator : ContainerIterator!(T); // Like Scala's 'type Iterator
> <: ContainerIterator[T]'
>
> T first();
> void remove(T);
> Iterator(T) firstIterator();
> void remove(Iterator(T));
> }
And this is also a form of metatyping. In this case, we want to declare
that the dependent type Iterator(T) has a metatype of
ContainerIterator(T) (that is, we minimize the set of possible values
(types) that Iterator(T) can have by defining the nature of those values
in terms of a metafunction...or, to put it another way, Iterator(T) is
an algebraic metatype).
> JFYI: There is a citation from 'Scalable Component Abstractions':
> \begin{quote}
> [...]
> So it seems that you reinvent the idea about 'abstract type members' ;)
Or rather, the CS community does not have a unified notion of metatyping
and is inventing terms as they go.
> [...]
> My proposal is to tell the compiler that 'this' in MyClass has type T or
> any of its subclass:
>
> template Demo(S, T) {
> class Dumpable is T {
> void dump(S stream) {
> stream.put( this ); // At this point 'this' is T.
> }
> }
> }
>
> class MyStream {
> void put( MyClass obj ) { ... }
> }
>
> class MyClass : Demo!( MyStream, MyClass ).Dumpable {
> ...
> }
> [...]
This particular example isn't very motivating because you would
typically just do this:
void dump(T)(S stream, T obj)
{
stream.put(obj);
}
In general, this is equivalent to the CRTP in C++, which has the general
form in D of:
class Derived : RecurringBase!(Derived)
{ ... }
As with any recurrence pattern, you have to be careful about what is
allowed and what isn't, or you may end up with an infinitely nested
definition. For instance, if RecurringBase(T) is allowed to derive from
T, you get the regress. However, D prevents this because T is a forward
reference at instantiation time.
I'm not sure what the value-add of the "self type" is when the CRTP is
available in D:
class Base(T) { T x; }
class Derived : Base!(Derived) { int x; }
void main() { Derived d; }
This is a perfectly valid D program according to dmd.
Dave
More information about the Digitalmars-d
mailing list