www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Minor problems with templates and mixins

reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
While D's template system is really excellent, it's still got minor 
problems. One is related to the current way to define function 
templates. The other is about mixins - they seem to have a really 
awkward nature.

* The two issues I present are by no means showstoppers, but rather 
language details that make it unpleasant to use in a few cases.


1. Can't overload functions inside templates, e.g. the following code 
won't work

# template foo(T) {
#     T foo(int  a) { return T.init; }
#     T foo(char a) { return T.init; }
# }
#
# void main() {
#     foo!(float)(1);
# }

Yeah, the spec mentions that to be able to write foo!(float) instead of 
foo!(float).foo, the template must have exactly one symbol in it, yet 
it's not very comfortable to work with.

It's possible to partially work around this limitation, e.g.

# private import std.stdio;
#
# template foo(T) {
#     static struct foo {
#         static T opCall(int  a) { writefln("int");  return T.init; }
#         static T opCall(char a) { writefln("char"); return T.init; }
#     }
# }
#
# void main() {
#     foo!(float)(1);
#     foo!(char)('a');
# }

But this idea breaks when the function template is meant to be a 
non-static member of some class.


2. The spec for mixins states "Unlike a template instantiation, a 
template mixin's body is evaluated within the scope where the mixin 
appears, not where the template declaration is defined. It is analogous 
to cutting and pasting the body of the template into the location of the 
mixin". Yet the following code doesn't compile:

# template A() {
#     template foo(T : int) {
#         void foo() {
#         }
#     }
# }
#
# template B() {
#     template foo(T : char) {
#         void foo() {
#         }
#     }
# }
#
# mixin A;
# mixin B;
#
# void main() {
#     foo!(char)();
# }

Also, aliasing the foo function template out of A doesn't work:

# alias A.foo foo;
# alias B.foo foo;


In both cases, conflicting symbols are reported by DMD. On the other 
hand, the following code does compile:

# template foo(T : int) {
#     void foo() {
#     }
# }
#
# template foo(T : char) {
#     void foo() {
#     }
# }

# void main() {
#     foo!(char)();
# }

Which clearly illustrates that mixins are not like copy and paste. But 
that's pretty obvious even due another sentence from the spec "A mixin 
has its own scope". Copy-and-paste doesn't.
IMHO, mixins should have their scope, but except for that, they should 
behave like parametrized copy and paste...


Similarly, the following code also produces errors:

# template foo(T) {
#     void foo(T x) {
#     }
# }
#
# alias foo!(int).foo     foo;
# alias foo!(char[]).foo  foo;
#
# void main() {
#     foo(1);
# }


Although this works just fine:

# void fooInt(int x) {}
# void fooStr(char[] x) {}
#
# alias fooInt foo;
# alias fooStr foo;
#
# void main() {
#     foo(1);
# }


Have I missed any important details ? Are there already good workarounds 
for these issues ?
All constructive criticism is welcome ;)



-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/M d-pu s+: a-->----- C+++$>++++ UL P+ L+ E--- W++ N++ o? K? w++ !O 
!M V? PS- PE- Y PGP t 5 X? R tv-- b DI- D+ G e>+++ h>++ !r !y
------END GEEK CODE BLOCK------

Tomasz Stachowiak  /+ a.k.a. h3r3tic +/
May 23 2006
parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Tom S skrev:
 While D's template system is really excellent, it's still got minor 
 problems. One is related to the current way to define function 
 templates. The other is about mixins - they seem to have a really 
 awkward nature.
 
 * The two issues I present are by no means showstoppers, but rather 
 language details that make it unpleasant to use in a few cases.
 
 
 1. Can't overload functions inside templates, e.g. the following code 
 won't work
 
 # template foo(T) {
 #     T foo(int  a) { return T.init; }
 #     T foo(char a) { return T.init; }
 # }
 #
 # void main() {
 #     foo!(float)(1);
 # }
 
 Yeah, the spec mentions that to be able to write foo!(float) instead of 
 foo!(float).foo, the template must have exactly one symbol in it, yet 
 it's not very comfortable to work with.

I posted about this a while ago too. For this case, the following workaround seems to work: template foo_(T) { T foo(int a) { return T.init; } T foo(char a) { return T.init; } } template foo(T) { alias foo_!(T).foo foo; } void main() { foo!(float)('a'); }
 It's possible to partially work around this limitation, e.g.

[snip]
 But this idea breaks when the function template is meant to be a 
 non-static member of some class.

The template-typedef(alias) workaround seems to work as a non-static member too. Regards, Oskar
May 23 2006
parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Oskar Linde wrote:
 I posted about this a while ago too. For this case, the following 
 workaround seems to work:
 
 template foo_(T) {
   T foo(int  a) { return T.init; }
   T foo(char a) { return T.init; }
 }
 
 template foo(T) {
   alias foo_!(T).foo foo;
 }
 
 void main() {
   foo!(float)('a');
 }

That's one cute hack ;) Thanks ! Anyhow, it shouldn't be necessary to use them if we want D to be a clean language... -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d-pu s+: a-->----- C+++$>++++ UL P+ L+ E--- W++ N++ o? K? w++ !O !M V? PS- PE- Y PGP t 5 X? R tv-- b DI- D+ G e>+++ h>++ !r !y ------END GEEK CODE BLOCK------ Tomasz Stachowiak /+ a.k.a. h3r3tic +/
May 23 2006