On Sat, Jul 31, 2010 at 21:17, Lutger <lutger.blijdest...@gmail.com> wrote:
> Jason Spencer wrote: > > > == Quote from Philippe Sigaud (philippe.sig...@gmail.com)'s article > >> --0016e6d58a039d35e2048c9aa7e2 > >> > >> I thought they could only be symbols. That is, an alias is a 'link', > > a sort > >> of pointer to a symbol: a template name, a module name, a function > >> name, etc. > > > > Whatever confidence you inspired by removing type from the list is > > quickly lost and more when you add module name--I hadn't thought of > > that! :) > I discovered it by error, IIRC. That and the fact that template-like statements like auto members = __traits(allMembers, std.stdio); work. Try it, print the result. Though I do not know what to do with it :) > > > > > >> Wisdom, I don't know, as I still feel like I'm exploring things. But > >> template constraints are there to limit what you can instantiate. > >> ... > >> Say I have a template that takes an alias, fun, and a type, T. > >> fun is supposed to be a function, but in fact why limit it to that? > >> What I need is for foo to be callable with a T. So let's test for > >> that: > >> auto doSomething(alias fun, T)(T t) > >> if (is(typeof( fun(T.init) ))) > >> { > >> // now that I'm here, I can freely use fun as a callable on any T > >> auto result = fun(t); > >> // ... > >> } > > > > I understand this example, and (most of) the mechanics of constraints. > > What I'm not so sure about is the recommended practice around their > > use. I see lot's of code that doesn't check those things. Suppose > > you left off the constraint and did: > > > > class Foo(U){} > > doSomething!(Foo, int)(3) > > > > it seems like there's a good chance you could get: > > > > auto result = Foo!(int); // type inferred from 3 > > > > (since this doesn't actually work like I'm saying, please conveniently > > imagine a similar case that does. :) > > Sure, but that would be quite a gotcha since you went from calling > something to > merely instantiating a template. Perhaps there are such gotcha's, I am not > aware > of them. > You can test for the alias to be a function (is(typeof(a) == function)), a delegate, or you can test for the presence of an opCall operator (the '()' operator), with __traits(hasMember, alias, "opCall") I think there should be a isCallable trait in Phobos... But in the opCall case, the gotcha is the class templates are not classes. Only instantiated classes are really classes. That is, given class C(T) { T t; T opCall(T u) { return t;} } __traits(hasMember, C, "opCall") will answer 'false'. That's normal, since C is not a class, but a template, of type void. __traits(hasMember, C!int, "opCall") will answer 'true'. > > > Even with your constraint, I'm not sure I feel any more comfortable. > > If it compiles in the body of doSomething, it will compile in the > > constraint--not sure I've added any value. > > Perhaps not in this case, but: > - constraints add documentation, such as isInputRange!T or IsCallable!fun > - you can add a constraint that may not be statically checked inside the > body of > the template. This way you can still reject template parameters considered > to be > invalid even if the template body *could* be instantiated with them. > I never thought of that. > - a constraint is a nice place to add better compiler error messages > Except the compiler just say it cannot instantiate the template NameWithConstraints. > > So how do you sleep at night not knowing if there's some funky syntax > > on somebody's template-that-takes-a-template which, when combined with > > some inference, might look like your function call on a value param? > > My initial reaction is to specify the hell out of the constraints, but > > I couldn't beat the feeling I was going overboard. I suspect that > > most people rely on the fact that most improper calls won't compile. > Yes. > > Maybe I'm still too new to the syntax to have a good feel for what > > will get caught, and what could interpreted by the compiler in > > multiple ways depending on the actual arguments. > I have nothing against long and well-documented constraints. I feel we are not using them as much as we could. With things like staticMap and such, you can do a lot! > > > > So, do folks write constraints to ensure that modules don't get passed > > to their templates? :) > > > > Jason > > Why not? As long as your module does the right thing, it may be used to > instantiate my template :) > You can try to define an isTemplate template, and a isModule template :-) Look at this: T foo(T)(T t) { return t;} class C(T) { T t; T opCall(T u) { return t;} } template Describe(alias a) { enum string Name = a.stringof; enum string Ident = __traits(identifier, a); } void main() { writeln(Describe!(foo).Name); // foo(T) writeln(Describe!(foo).Ident); // foo writeln(Describe!(C).Name); // C(T) writeln(Describe!(C).Ident); // C writeln(Describe!(C!int).Name); // C (really, I thought that would be C!(int). I remember devising a way to extract the types from a template writeln(Describe!(C!int).Ident); // C writeln(Describe!(Describe).Name); // Calling it on itself! Describe(alias a) writeln(Describe!(Describe).Ident); // Describe writeln(Describe!(std.stdio).Name); // module stdio Strange, no std. in sight. writeln(Describe!(std.stdio).Ident); // stdio } So, inside a template a.stringof gives "module a" if a is a module, except it seems to cut anything before a dot. I do not like relying on .stringof, because it's undocumented and not always perfectly coherent. But a pragmatic implementation of isModule could be: template isModule(alias symbol) { enum bool isModule = (symbol.stringof[0..7] == "module "); } :-) Philippe