On Sat, Jul 31, 2010 at 21:17, Lutger <lutger.blijdestijn@gmail.com> wrote:
Jason Spencer wrote:

> == Quote from Philippe Sigaud (philippe.sigaud@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