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