www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Templates convariance

reply michal.minich gmail.com writes:
Now we have interfaces covariance working in D, but Templates covariance is not
implemented yet. Simply:

while you can write this:
Shape s = new Circle();

This fails to compile:
List!(Shape) sList = new List!(Circle)();

To show usefullness of this functionality, here is an example:

class Shape
{
void render()
{
printf ("Shape\n");
}
}

class Circle : Shape
{
void render()
{
printf ("Circle\n");
}
}


class List (T)
{
T item; // for simplicity, just one item possible in this list 
}

void renderShapes (List!(Shape) shapesList)
{
shapesList.item.render();
}

void main()
{
List!(Shape) shapesList = new List!(Shape) ();
shapesList.item = new Shape();

List!(Circle) circlesList = new List!(Circle) ();
circlesList.item = new Circle();

renderShapes ( shapesList ); // this works
//renderShapes ( circlesList ); // *1 - does not compile
renderShapes ( cast(List!(Shape))circlesList ); // *2 - access violation

}

// *1
//test.d(37): function test.renderShapes (List) does not match argument types
(List)
//test.d(37): cannot implicitly convert expression (circlesList) of type
test.List!(Circle).List to test.List!(Shape).List

// *2 application compiles, but when run, crashes with access violation


I think that implementation of this functionality can be very difficult, but
simply said, templates should be covariant if all its instantiation parameters
are covariant.
Jun 20 2006
next sibling parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Frankly, I wouldn't like to see that 'template covariance' in D. It 
would mess things up... How about doing something like:

class List(T) {
     template asList(T1) {
         .List!(T1) asList() {
             static if (is(T : T1)) {
                 return cast(.List!(T1))cast(void*)this;
             } else {
                 return null;  // or static assert (false)
             }
         }
     }
}


then you might write:

auto circleList = new List!(Circle);
auto shapeList  = circleList.asList!(Shape);



-- 
Tomasz Stachowiak  /+ a.k.a. h3r3tic +/
Jun 20 2006
next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Tom S wrote:
             static if (is(T : T1)) {
                 return cast(.List!(T1))cast(void*)this;
             } else {

Btw, this test would have to be more complex in case of primitive types, but then the 'covariance' couldn't quite work either. -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
Jun 20 2006
prev sibling parent reply michal.minich gmail.com writes:
Thank you for casting solution. It should works perfect for my problem. But
could you explain me how this can mess things up?



In article <e79amd$2kil$1 digitaldaemon.com>, Tom S says...
Frankly, I wouldn't like to see that 'template covariance' in D. It 
would mess things up... How about doing something like:

class List(T) {
     template asList(T1) {
         .List!(T1) asList() {
             static if (is(T : T1)) {
                 return cast(.List!(T1))cast(void*)this;
             } else {
                 return null;  // or static assert (false)
             }
         }
     }
}


then you might write:

auto circleList = new List!(Circle);
auto shapeList  = circleList.asList!(Shape);



-- 
Tomasz Stachowiak  /+ a.k.a. h3r3tic +/

Jun 20 2006
parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
michal.minich gmail.com wrote:
 Thank you for casting solution. It should works perfect for my problem. But
 could you explain me how this can mess things up?

Basically, the fact that Foo is castable to Bar doesn't mean that Baz!(Foo) should be castable to Baz!(Bar). Their implementation may be totally different based on some static if's and other fancy stuff. IMO they are two totally separate classes. If that kind of invariance would be to be allowed, then one would sometimes need to specify explicitly that a given Baz!(Bar) should not be casted to a Baz!(Foo). And in that case, why not reverse the idea and just use the method I provided in the remaining cases ? :) -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
Jun 20 2006
prev sibling parent pragma <pragma_member pathlink.com> writes:
In article <e799dr$2igf$1 digitaldaemon.com>, michal.minich gmail.com says...
[snip]
class List (T)
{
T item; // for simplicity, just one item possible in this list 
}

void renderShapes (List!(Shape) shapesList)
{
shapesList.item.render();
}

Usually when I come across the need for something like that, I typically throw in a common base class or interface for my template instances, so they are castable from one to the other. interface IShapeList{ void renderShapes(IShapeList list); } class ShapeList(T): IShapeList{ /*...*/ } As a side-effect, the above is now very friendly to use with non-template based IShapeLists. ;) I'm not a metaprogramming expert by any means, but I don't think covariance with templates can be solved using a rule general enough to be implemented (easily) in the compiler itself. The problem is that templates allow one to create interfaces derived from supplied types that can be composed in so many arbitrary ways. - EricAnderton at yahoo
Jun 20 2006