Thread overview
Eponymous template with full template syntax
Jul 01, 2013
Ali Çehreli
Jul 01, 2013
Jonathan M Davis
Jul 01, 2013
Maxim Fomin
Jul 01, 2013
Ali Çehreli
Jul 01, 2013
monarch_dodra
Jul 01, 2013
Ali Çehreli
Jul 02, 2013
monarch_dodra
July 01, 2013
I think main's second line used to work:

template isSmall(T)
{
    enum isSmall = (T.sizeof < 12345);
}

void main()
{
    static assert(isSmall!int);          // <-- the usual syntax works

    static assert(isSmall!int.isSmall);  // <-- compilation ERROR
}

Error: template deneme.isSmall does not match any function template declaration. Candidates are:
       deneme.isSmall(T)
Error: template deneme.isSmall(T) cannot deduce template function from argument types !()(bool)

Am I imagining it? I don't have a problem with it. :) Was the change intentional?

Ali
July 01, 2013
On Monday, July 01, 2013 11:15:04 Ali Çehreli wrote:
> I think main's second line used to work:
> 
> template isSmall(T)
> {
> enum isSmall = (T.sizeof < 12345);
> }
> 
> void main()
> {
> static assert(isSmall!int); // <-- the usual syntax works
> 
> static assert(isSmall!int.isSmall); // <-- compilation ERROR
> }
> 
> Error: template deneme.isSmall does not match any function template
> declaration. Candidates are:
> deneme.isSmall(T)
> Error: template deneme.isSmall(T) cannot deduce template function from
> argument types !()(bool)
> 
> Am I imagining it? I don't have a problem with it. :) Was the change intentional?

I'm not aware of it ever having worked, but given that the whole point of eponymous templates is that they be replaced with the symbol carrying their name and _everything_ else in the template is hidden, I would think that what you're seeing is correct behavior.

isSmall!int is replaced with the isSmall within isSmall!int, and isSmall.isSmall makes no sense, so if that was allowed before, I'd definitely argue that disallowing it was a bug fix.

- Jonathan m Davis
July 01, 2013
On Monday, 1 July 2013 at 18:15:06 UTC, Ali Çehreli wrote:
> I think main's second line used to work:
>
> template isSmall(T)
> {
>     enum isSmall = (T.sizeof < 12345);
> }
>
> void main()
> {
>     static assert(isSmall!int);          // <-- the usual syntax works
>
>     static assert(isSmall!int.isSmall);  // <-- compilation ERROR
> }
>
> Error: template deneme.isSmall does not match any function template declaration. Candidates are:
>        deneme.isSmall(T)
> Error: template deneme.isSmall(T) cannot deduce template function from argument types !()(bool)
>
> Am I imagining it? I don't have a problem with it. :) Was the change intentional?
>
> Ali

I think that this probably worked as early as in the end of 2011 but I can be wrong as don't remember exactly.

It seems that dmd recognizes isSmall!int.isSmall as potential UFCS property, converts isSmall!int to bool and tries to issue call isSmall(bool) and fails, because that template does not define any function.
July 01, 2013
On Mon, 01 Jul 2013 14:15:04 -0400, Ali Çehreli <acehreli@yahoo.com> wrote:

> I think main's second line used to work:
>
> template isSmall(T)
> {
>      enum isSmall = (T.sizeof < 12345);
> }
>
> void main()
> {
>      static assert(isSmall!int);          // <-- the usual syntax works
>
>      static assert(isSmall!int.isSmall);  // <-- compilation ERROR
> }
>
> Error: template deneme.isSmall does not match any function template declaration. Candidates are:
>         deneme.isSmall(T)
> Error: template deneme.isSmall(T) cannot deduce template function from argument types !()(bool)
>
> Am I imagining it? I don't have a problem with it. :) Was the change intentional?

I think it used to work, and I think the change was intentional.  I also "discovered" this not too long ago.

-Steve
July 01, 2013
On 07/01/2013 12:03 PM, Maxim Fomin wrote:

> I think that this probably worked as early as in the end of 2011 but I
> can be wrong as don't remember exactly.

To answer Jonathan's question as well, it must have worked because I see it in code that is definitely tested when it was written.

> It seems that dmd recognizes isSmall!int.isSmall as potential UFCS
> property, converts isSmall!int to bool and tries to issue call
> isSmall(bool) and fails, because that template does not define any
> function.

That explains it. :) Let's play with it a little:

import std.stdio;

template isSmall(T)
{
    enum isSmall = (T.sizeof < 12345);

    struct S
    {
        T m;
    }
}

struct S
{
    int[10] i;
}

void main()
{
    writeln(isSmall!int);
    writeln(isSmall!int.S.init);
    writeln(isSmall!int.S);
}

First of all, apparently a template can include a definition with the same name but I still cannot type isSmall!int.isSmall. I guess the above is still an eponymous template and isSmall!int still means isSmall!int.isSmall.

Now guess what the last two lines print. :) isSmall!int.S is *not* the S that is included in the template! Here is the output:

true
S([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
S([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

The last line is actually an anonymous struct object of type S (the S that is defined at module level). That is confusing.

Ali

July 01, 2013
On Monday, 1 July 2013 at 20:28:28 UTC, Ali Çehreli wrote:
> On 07/01/2013 12:03 PM, Maxim Fomin wrote:
>
> > I think that this probably worked as early as in the end of
> 2011 but I
> > can be wrong as don't remember exactly.
>
> To answer Jonathan's question as well, it must have worked because I see it in code that is definitely tested when it was written.
>
> > It seems that dmd recognizes isSmall!int.isSmall as potential
> UFCS
> > property, converts isSmall!int to bool and tries to issue call
> > isSmall(bool) and fails, because that template does not
> define any
> > function.
>
> That explains it. :) Let's play with it a little:
>
> import std.stdio;
>
> template isSmall(T)
> {
>     enum isSmall = (T.sizeof < 12345);
>
>     struct S
>     {
>         T m;
>     }
> }
>
> struct S
> {
>     int[10] i;
> }
>
> void main()
> {
>     writeln(isSmall!int);
>     writeln(isSmall!int.S.init);
>     writeln(isSmall!int.S);
> }
>
> First of all, apparently a template can include a definition with the same name but I still cannot type isSmall!int.isSmall. I guess the above is still an eponymous template and isSmall!int still means isSmall!int.isSmall.
>
> Now guess what the last two lines print. :) isSmall!int.S is *not* the S that is included in the template! Here is the output:
>
> true
> S([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
> S([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
>
> The last line is actually an anonymous struct object of type S (the S that is defined at module level).

I though UFCS wasn't possible with constructors? *That* very usecase is one of the reasons why. Shouldn't that be an accepts-invalid?

> That is confusing.

UFCS construction: Yes. The rest, not so much:

The idea is that once a template is "eponymous", it *fully* becomes the eponymous function/type/value (s). Every other function, regardless of public/private*, simply seizes to exist to the outside world. You can't make a "qualified" call to an eponymous template, because the "qualification" is already the call. Long story short, it's not mix and match: Either you have a normal template, or you have an something eponymous, not a bit of both:

*What qualifies for eponymous template is kind of "buggy", since what actually qualifies is not exactly what the spec says. Still, *once* something is considered "qualified" by the implementation, then it is fully eponymous.
July 01, 2013
On 07/01/2013 02:10 PM, monarch_dodra wrote:

>> That is confusing.
>
> UFCS construction: Yes.

I *think* I did not know it but I can't be sure. :)

struct S
{
    int i;
}

void main()
{
    static assert (S(42) == 42.S);
}

It works with 2.063 (v2.064-devel-a1a1537 too).

> The rest, not so much:
>
> The idea is that once a template is "eponymous", it *fully* becomes the
> eponymous function/type/value (s). Every other function, regardless of
> public/private*, simply seizes to exist to the outside world. You can't
> make a "qualified" call to an eponymous template, because the
> "qualification" is already the call.

A single definition with the same name makes it an eponymous template. I used to think that the template should also have a single definition.

So, currently other definitions act as implementation details of the template. The following template sees the local S, not the module-level one:

struct S
{
    int[10] i;
}

template epo(T)
{
    size_t epo()
    {
        return S.sizeof;  // <-- epo.S, not .S
    }

    struct S
    {
        int i;
    }

    double foo()
    {
        return 1.5;
    }
}

void main()
{
    assert(epo!int() == int.sizeof);  // <-- yes, epo.S

    mixin epo!int;
    assert(foo() == 1.5);
}

Also note that mixing in the template is still possible but it is an orthogonal feature anyway.

Ali

July 02, 2013
On Monday, 1 July 2013 at 22:46:55 UTC, Ali Çehreli wrote:
> On 07/01/2013 02:10 PM, monarch_dodra wrote:
>
> >> That is confusing.
> >
> > UFCS construction: Yes.
>
> I *think* I did not know it but I can't be sure. :)

AH... I looked at the threads some more: I was actually thinking about a proposal that wanted (just like UFCS), to allow non-intrusively adding constructors.

So I guess UFCS and constructors are fair game? I do not like this at all...