www.digitalmars.com         C & C++   DMDScript  

D - template functions

reply Sean Kelly <sean ffwd.cx> writes:
One feature of D I haven't completely adjusted to is the need to 
explicitly specify each parameter of template methods.  Would it be too 
complicated to allow template parameter discovery somewhat similar to 
how it works in C++?

template foo( Ty ) {
     int foo( Ty val ) {
         return 1;
     }
}

int main() {
     int i = foo!(int)( 1 ); // A: current
     int j = foo!( 'c' ); // B: optional?
}

I'd like to be able to do something like in B.  The obvious catch is 
that more complex templates involving classes and the like could make 
such discovery difficult to impossible.  Comments?


Sean
Feb 22 2004
parent reply Sam McCall <tunah.d tunah.net> writes:
Sean Kelly wrote:
 template foo( Ty ) {
     int foo( Ty val ) {
         return 1;
     }
 }
 
 int main() {
     int i = foo!(int)( 1 ); // A: current
     int j = foo!( 'c' ); // B: optional?
 }

a function foo is a symbol conflict anyways).
 I'd like to be able to do something like in B.  The obvious catch is 
 that more complex templates involving classes and the like could make 
 such discovery difficult to impossible.  Comments?

type parameter must be the type of at least one parameter, no non-trivial overloading), in every other case you'd have to specify manually. This isn't ideal but it's so much better than what we've got now, and seems to be relatively simple to implement. Sam
Feb 22 2004
parent reply C <dont respond.com> writes:
 I'd be happy with limited discovery, as I posted about earlier (every =

 type parameter must be the type of at least one parameter, no =

 non-trivial overloading)

That sounds good to me, though I really don't know what the fuss is abou= t = on having to specify type . Sure its nice ( type deduction ) but are yo= u = really all that lazy ? :P C On Mon, 23 Feb 2004 13:53:52 +1300, Sam McCall <tunah.d tunah.net> wrote= :
 Sean Kelly wrote:
 template foo( Ty ) {
     int foo( Ty val ) {
         return 1;
     }
 }

 int main() {
     int i =3D foo!(int)( 1 ); // A: current
     int j =3D foo!( 'c' ); // B: optional?
 }


 a function foo is a symbol conflict anyways).

 I'd like to be able to do something like in B.  The obvious catch is =


 that more complex templates involving classes and the like could make=


 such discovery difficult to impossible.  Comments?


 type parameter must be the type of at least one parameter, no =

 non-trivial overloading), in every other case you'd have to specify =

 manually. This isn't ideal but it's so much better than what we've got=

 now, and seems to be relatively simple to implement.
 Sam

-- = Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Feb 22 2004
next sibling parent reply Sean Kelly <sean ffwd.cx> writes:
C wrote:
 I'd be happy with limited discovery, as I posted about earlier (every 
 type parameter must be the type of at least one parameter, no 
 non-trivial overloading)

That sounds good to me, though I really don't know what the fuss is about on having to specify type . Sure its nice ( type deduction ) but are you really all that lazy ? :P

Perhaps it's a hold-over from C++, but I like having a unified calling convention for functions. Also, there are times when template parameters might be non-obvious to the caller, though I'll grant that in D this could be solved via nested templates. But I'm undecided about whether I'd like this in D, as the template declaration method is quite different from that in C++. I just thought of a way around explicit type specification. Referring to my original example, this is a working substitute for B: char c = 'c'; int j = foo!( typeof( c ) )( c ); This is ovbiously more verbose, but it does help avoid maintenance problems regarding explicitly specified types. I'll have to think some more about whether type discovery might allow for something that is difficult to accomplish without it. For some reason I thought I'd come up with an example but if I did it currently escapes me. Sean
Feb 22 2004
parent reply Sam McCall <tunah.d tunah.net> writes:
 That sounds good to me, though I really don't know what the fuss is 
 about on having to specify type .  Sure its nice ( type deduction ) 
 but are you really all that lazy ? :P


Collection!(String) bar; foo.addAll!(MyCollection!(String))(bar); versus foo.addAll(bar); Personally find the first one moderately unwritable and perfectly unreadable ;)
 char c = 'c';
 int j = foo!( typeof( c ) )( c );
 
 This is ovbiously more verbose, but it does help avoid maintenance 
 problems regarding explicitly specified types.

which type argument, which the compiler already knows about. If something mechanical like this can do the job, why should you actually have to type it? Sam
Feb 22 2004
parent reply "Ben Hinkle" <bhinkle4 juno.com> writes:
"Sam McCall" <tunah.d tunah.net> wrote in message
news:c1bqrm$2fvv$1 digitaldaemon.com...
 That sounds good to me, though I really don't know what the fuss is
 about on having to specify type .  Sure its nice ( type deduction )
 but are you really all that lazy ? :P


Collection!(String) bar; foo.addAll!(MyCollection!(String))(bar); versus foo.addAll(bar); Personally find the first one moderately unwritable and perfectly unreadable ;)

My first try to investigate this was to use: class Collection(T:Object) { void addAll(Collection!(Object) x) {} } and then hope that Collection!(String) could be implicitly cast to Collection!(Object) but it failed to compile the addAll declaration. This looks to me like a bug in class template syntax. So my second attempt was interface Collection { void addAll(Collection x); } class LinkedList(T): Collection { void addAll(Collection x){} } and this worked fine when called with Collection foo = new LinkedList!(String); Collection bar = new LinkedList!(String); foo.addAll(bar); So my first guess is that templates might not be the right way to do what you want to do. That doesn't mean there isn't a problem with templates but I'm not sure this is a great example.
 char c = 'c';
 int j = foo!( typeof( c ) )( c );

 This is ovbiously more verbose, but it does help avoid maintenance
 problems regarding explicitly specified types.

which type argument, which the compiler already knows about. If something mechanical like this can do the job, why should you actually have to type it? Sam

Feb 23 2004
next sibling parent reply Sean Kelly <sean ffwd.cx> writes:
Ben Hinkle wrote:
 So my first guess is that templates might not be the right way to do what
 you want to do. That doesn't mean there isn't a problem with templates
 but I'm not sure this is a great example.

I think it's more an example of how the syntax can get complicated rather than being an example of something you'd probably want to do very often. Sean
Feb 23 2004
next sibling parent "Ben Hinkle" <bhinkle4 juno.com> writes:
"Sean Kelly" <sean ffwd.cx> wrote in message
news:c1derl$2bif$1 digitaldaemon.com...
 Ben Hinkle wrote:
 So my first guess is that templates might not be the right way to do


 you want to do. That doesn't mean there isn't a problem with templates
 but I'm not sure this is a great example.

I think it's more an example of how the syntax can get complicated rather than being an example of something you'd probably want to do very often.

Over lunch I realized the reason for not using a straightforward interface is that it loses the contained type T. So I think his example is actually a good one for any generic, efficient add method from one container type to another. If you start making assumptions about the source or destination types then my posted code would be ok - but it wouldn't work well for the general case.
 Sean

Feb 23 2004
prev sibling parent reply "Ben Hinkle" <bhinkle4 juno.com> writes:
"Sean Kelly" <sean ffwd.cx> wrote in message
news:c1derl$2bif$1 digitaldaemon.com...
 Ben Hinkle wrote:
 So my first guess is that templates might not be the right way to do


 you want to do. That doesn't mean there isn't a problem with templates
 but I'm not sure this is a great example.

I think it's more an example of how the syntax can get complicated rather than being an example of something you'd probably want to do very often. Sean

Take Two. Collections are just waay to much fun to putter around with :-) Here's an example that results in calls that look like TmixCollectionTypes!(Object,String).addAll(foo,bar); when you want to copy from one contained type to another. More details: interface Collection(T) { // a cheesy Collection T getItem(); void addItem(T x); } template TmixCollectionTypes(S,T) { void addAll(Collection!(S) x, Collection!(T) y) { x.addItem(y.getItem()); // you get the idea... } } class LinkedList(T): Collection!(T) { // a cheesy list T getItem() { return item; } void addItem(T x) { item=x; } T item; } class String {} int main(char[][] argv) { Collection!(Object) foo = new LinkedList!(Object); Collection!(String) bar = new LinkedList!(String); TmixCollectionTypes!(Object,String).addAll(foo,bar); return 0; }
Feb 23 2004
parent reply Sam McCall <tunah.d tunah.net> writes:
Ben Hinkle wrote:

 Take Two. Collections are just waay to much fun to putter around with :-)
 Here's an example that results in calls that look like
   TmixCollectionTypes!(Object,String).addAll(foo,bar);

might be a better way for some collections than add() one item at a time. I guess my problem with these solutions is I want templates to help me - I want to give my variables more descriptive types and in exchange I want better type checking. I don't want to care about templates until there's a problem. Sam
Feb 23 2004
next sibling parent reply "Ben Hinkle" <bhinkle4 juno.com> writes:
"Sam McCall" <tunah.d tunah.net> wrote in message
news:c1dm0t$2ob1$1 digitaldaemon.com...
 Ben Hinkle wrote:

 Take Two. Collections are just waay to much fun to putter around with


 Here's an example that results in calls that look like
   TmixCollectionTypes!(Object,String).addAll(foo,bar);

might be a better way for some collections than add() one item at a time.

Note it would have to be a static member function - see the section "Limitations" in the Templates doc. Virtual template member functions are mind-bending (at least to me) since there would have to be vtable slots for every possible template instantiation.
 I guess my problem with these solutions is I want templates to help me -
 I want to give my variables more descriptive types and in exchange I
 want better type checking. I don't want to care about templates until
 there's a problem.

 Sam

Feb 23 2004
parent Sam McCall <tunah.d tunah.net> writes:
Ben Hinkle wrote:

Here's an example that results in calls that look like
  TmixCollectionTypes!(Object,String).addAll(foo,bar);

Nice, but this should be a member function. More specifically, there might be a better way for some collections than add() one item at a time.

Note it would have to be a static member function - see the section "Limitations" in the Templates doc.

 Virtual template member functions
 are mind-bending (at least to me) since there would have to be vtable
 slots for every possible template instantiation.

template foo!(A,B:C) { void foo(A a,B b); } with template foo!(D,E:C) { void foo(D d,E e); } but I'm sure it quickly gets too complicated. Sam
Feb 23 2004
prev sibling parent Sean Kelly <sean ffwd.cx> writes:
Sam McCall wrote:
 Nice, but this should be a member function. More specifically, there 
 might be a better way for some collections than add() one item at a time.

In some cases this couldn't be a member function. Arrays, for example.
 I guess my problem with these solutions is I want templates to help me - 
 I want to give my variables more descriptive types and in exchange I 
 want better type checking. I don't want to care about templates until 
 there's a problem.

I'm considering using free-functions to return iterators much like begin, end, etc, as they exist in the C++ standard library. Not having to explicitly specify template arguments for these functions would be a nice perk, but I think it will work out either way. Sean
Feb 23 2004
prev sibling parent Sam McCall <tunah.d tunah.net> writes:
Collection!(Object) foo;
Collection!(String) bar;

foo.addAll!(MyCollection!(String))(bar);
versus
foo.addAll(bar);

Personally find the first one moderately unwritable and perfectly
unreadable ;)

class Collection(T:Object) { void addAll(Collection!(Object) x) {} } and then hope that Collection!(String) could be implicitly cast to Collection!(Object) but it failed to compile the addAll declaration.

S[] where T is a subclass of S, and the result is the possibility that t[0]=a; gives you a runtime exception, if t a T[] variable that points to an S[], and a is a T but not an S. As much as I'd like it to be most of the time, Foo!(S : T) is-not-a Foo!(T). In this case, this would break type checking by allowing you to add any Object. Of course it won't work for ints either.
 So my first guess is that templates might not be the right way to do what
 you want to do. That doesn't mean there isn't a problem with templates
 but I'm not sure this is a great example.

seems to be pretty well done using templates. For the record, my ideal definition would be interface Collection(T) { template addAll(S:T) { void addAll(Collection!(S) c); } } class LinkedList(T) : Collection!(T) { template addAll(S:T) { void addAll(Collection!(S) c) { foreach(S s;c) add(s); } } } (Actually there'd be a List interface in between). I'm not sure if template addAll(S:T) or template addAll(S:T,Collection!(S)) or template addAll(Collection!(S:T)) are the most likely to ever be supported by deduction, but the syntax foo.addAll!(bar.T)(bar) isn't much better than foo.addAll!(typeof(bar))(bar) in terms of readability. On an unrelated note having a template around every templated function is awkward, can we have some sugar please? ;) It'd also make me less nervous about exactly how overriding works when it's inside a template.
 This looks to me like a bug in class template syntax.
 So my second attempt was
   interface Collection {
     void addAll(Collection x);
   }
   class LinkedList(T): Collection {
     void addAll(Collection x){}
   }
 and this worked fine when called with
   Collection foo = new LinkedList!(String);
   Collection bar = new LinkedList!(String);
   foo.addAll(bar);

implement it. void addAll(Collection x) { foreach(T item;x){ // WTF is T? add(item); } } Even if you decide collections only hold objects, it isn't typesafe. Sam
Feb 23 2004
prev sibling parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
It's not a question of sloth. It's about whether, and how easily, one can
write generic code.

 I'd be happy with limited discovery, as I posted about earlier (every
 type parameter must be the type of at least one parameter, no
 non-trivial overloading)

That sounds good to me, though I really don't know what the fuss is about on having to specify type . Sure its nice ( type deduction ) but are you really all that lazy ? :P C On Mon, 23 Feb 2004 13:53:52 +1300, Sam McCall <tunah.d tunah.net> wrote:
 Sean Kelly wrote:
 template foo( Ty ) {
     int foo( Ty val ) {
         return 1;
     }
 }

 int main() {
     int i = foo!(int)( 1 ); // A: current
     int j = foo!( 'c' ); // B: optional?
 }

a function foo is a symbol conflict anyways).
 I'd like to be able to do something like in B.  The obvious catch is
 that more complex templates involving classes and the like could make
 such discovery difficult to impossible.  Comments?

type parameter must be the type of at least one parameter, no non-trivial overloading), in every other case you'd have to specify manually. This isn't ideal but it's so much better than what we've got now, and seems to be relatively simple to implement. Sam

-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Feb 22 2004