Type polymorphism and type variance

js.mdnq js_adddot+mdng at gmail.com
Wed Dec 5 09:51:57 PST 2012


On Wednesday, 5 December 2012 at 03:59:29 UTC, Ali Çehreli wrote:
> On 12/04/2012 06:42 PM, js.mdnq wrote:
> > One thing I've always struggled with in oop is how to deal
> with
> > storing generic types.
> >
> > A very simple example is, suppose you had to design a way to
> > store generic types.
> >
> > class myGtype(T) { }
> >
> > ...
> >
> > myGType[] gcollection; // should store various types such as
> > myGtype!int, myGtype!myobj, etc.., possibly even other things
> > like myotherobj, etc..
> >
> > Obviously we can't store different types in a homogenous
> array.
> > I know D has the ability to use variant types that basically
> > overcome this. I imagine it is very inefficient to do it this
> way?
> >
> > In my mind, it seems like one could never get around the issue
> > without storing type information along with the data because
> the
> > compiler will eventually need to know the type information to
> > know how to deal with the data?
> >
> > e.g.,
> >
> > auto x = gcollection[i]; // x's type is not determined
>
> What do you expect to do with x? Unless there is a common 
> interface the compiler cannot compile the code without knowing 
> the type of x.
>
> Note that even myGtype!int and myGtype!double are completely 
> different types with completely different capabilities.
>
> > auto x = cast(myGtype!int)gcollection[i]; // x's type is
> forced
> > to be myGtype!int, but may not be if gcollection is
> heterogeneous.
> >
> > If we stored type information along with the data then we
> could
> > use it to cast to the correct type and make the compiler
> happy.
>
> Alas, the compiler is not available at runtime.
>
> > Are there any direct oop ways to do this.
>
> The easiest way in OOP would be interfaces and polymorphism. 
> The myGtype class template below implements the MyInterface 
> interface and it enables us to put different types of objects 
> in a collection.
>
> import std.stdio;
>
> interface MyInterface
> {
>     void foo();
>     MyInterface dup();
> }
>
> class myGtype(T) : MyInterface
> {
>     void foo()
>     {
>          specialOperationFor!T();
>     }
>
>     myGtype dup()
>     {
>         return new myGtype();
>     }
> }
>
> void specialOperationFor(T : double)()
> {
>     writefln("Special operation for %s", T.stringof);
> }
>
> struct S
> {
>     int i;
> }
>
> void specialOperationFor(T : S)()
> {
>     writefln("Special operation for %s", T.stringof);
> }
>
> void main()
> {
>     MyInterface[] objects;
>
>     objects ~= new myGtype!double();
>     objects ~= new myGtype!int();
>     objects ~= new myGtype!S();
>
>     foreach (o; objects) {
>         o.foo();
>     }
>
>     // We can even copy them and the copies have the correct 
> types:
>     foreach (i, o; objects) {
>         auto c = o.dup();
>         writefln("Using the copy of item %s", i);
>         c.foo();
>     }
> }
>
> The output:
>
> Special operation for double
> Special operation for int
> Special operation for S
> Using the copy of item 0
> Special operation for double
> Using the copy of item 1
> Special operation for int
> Using the copy of item 2
> Special operation for S
>
> Ali

[Sorry about all the dups, it seems many times lately the forum 
is not accepting my posts, seems like it went through this time 
even though it said it didn't]

Yeah, I basically understand that Ali, I wrote another post that 
updated what I was doing that was more pertinent.

Your method is akin to just storing the objects as object types 
but you end up wrapping them in a type to encapsulate the 
template. You did answer my question but unfortunately I 
basically asked the wrong question(or at least too simplified).

---------------------

I am trying to create a direct acyclic graph where nodes are 
objects and edges are functions between the nodes.

R[T in (T1, T2,...)][F] nodes;

so nodes[somenode] is an array of objects of types that depend on 
the specific nodes.

nodes[somenode][1] will be, say, a function from somenode to 
nodes[somenode][1].

These functions will take the nodes, which can be of different 
types, and compute some value on them.

R will be a function ptr that takes two nodes. (but it is variant)




e.g.,

myNode!A n1;
myNode!B n2;

bool function(myNode!A, myNode!B) compn1n2;

compn1n2(n1,n2);

is the basic idea. But every pair of connected nodes will 
possibly have a different function associated with it.

While I can use a common interface of the nodes and even that of 
the edges I don't see how I can pass around data between the two 
without resorting to passing objects and storing type information.


If this is possible maybe you can extend your example

class myGtype(T) : MyInterface
{
     T Value;   // <-------- added
     void foo()
     {
          specialOperationFor!T();
     }

     myGtype dup()
     {
         return new myGtype();
     }
}

...

     foreach (o; objects) {
         o.foo();
     }

but instead I need to do something like

auto value = o[0].foo(o[1]);

where value is then some computation between o[0] and o[1].

(in general I'll have to random objects of a common interface 
stored in my array that I want to do a computation on using a 
function associated with them)

Maybe all that is needed is another level in encapsulation to 
hide away the template parameters sort of like what you did? (and 
it will need to be done not only on the nodes but the edges to be 
able to store them too)

Thanks for the help.

(if your having trouble understanding the problem then just think 
of how you would efficiently store the nodes and edges of a 
directed acyclic graph. Each node is of a somewhat arbitrary type 
and edges are functions over the nodes that do computations, 
possibly changing the nodes "value". These all need to be stored 
someway so that a full computation chain can be carried out(the 
graph structure is arbitrary).)








More information about the Digitalmars-d-learn mailing list