January 31, 2020
On 1/30/20 9:10 AM, ShadoLight wrote:

> Why does the 'classical' template calling convention not work anymore in this case? (if the template name and function name are different it obviously still works). Note the templates were not defined in the simplified 'Eponymous Trick' style i.e.:
> 

I know your question is pretty much answered, but to give you some historical perspective, the long form WAS allowed in the past (it may still be that way in D1), and I think there were ambiguity problems as described. So you can no longer do that.

It was actually much more restrictive before -- e.g. in order to do an eponymous template, you could have no other members in the template.

Probably we should adjust the examples to remove those that no longer work.

-Steve
February 02, 2020
On Friday, 31 January 2020 at 15:37:06 UTC, Steven Schveighoffer wrote:
> On 1/30/20 9:10 AM, ShadoLight wrote:
>

> but to give you some historical perspective...

[..]

>
> It was actually much more restrictive before -- e.g.  in order to do an eponymous template, you could have no other members in the template.

Thanks for the historical perspective Steve. Appreciated.

When you refer to 'other members' in the eponymous template, I notice that they are indeed allowed, but are effectively hidden (if they have a different name from the template name) inside the eponymous template, correct?

For example, this works fine in 'classical' template style - all members are 'public' and accessible:

template bar(T) {
    T z;                  //Allowed...
    void b1(T x){z+=x;}   //Allowed...
    void b2(T x){z-=x;}   //Allowed...
}
void main()
{

    bar!(int).b1(4);    //Works..
    writeln(bar!int.z); //Works..
    bar!(int).b2(5);    //Works..
    writeln(bar!int.z); //Works..
}


For eponymous templates, only members with the overloaded template identifier are accessible 'from the outside'.


template foo(T) {
    T z;                  //Allowed...
    void foo(T x){f1(x);} //Allowed...
    T foo(){return z;}    //Allowed...
    void f1(T x){z+=x;}   //Allowed...
}


void main()
{
    foo(3);              //Works..
    foo(4);              //Works..
    writeln(foo!int.z);  //onlineapp.d(21): Error: no property z for type int
    foo!int.f1(3);       //onlineapp.d(21): Error: no property f1 for type int
    writeln(foo!int());  //Works..
}

So the eponymous template invocation has to use the eponymous 'trick' and only overloaded members are accessible.

Not bad and definitely an improvement , but I still find the inconsistency jarring... IIUC this 'ambiguity' would have been avoidable if template argument braces were not optional, right?

I do know this horse has bolted, but personally I think it D made a mistake to make the braces on compile-/run-time arguments on template/function invocation optional in the zero/one argument case.

It is not even completely symmetric between compile- and run-time: in compile time you can leave the braces off in the case of zero and one arguments, but for run-time only for zero arguments.

For want of the effort to type 2 braces, a fair bit of complexity and some inconsistency has been introduced into the language. And I remember that it actually made code harder to read when I started out in D, not easier.

I support the dropping of the braces in the case of property functions, but that use case is quite clear-cut and I think easy to handle based on the @property annotation.

February 02, 2020
On Sunday, 2 February 2020 at 13:01:26 UTC, ShadoLight wrote:
> Not bad and definitely an improvement , but I still find the inconsistency jarring... IIUC this 'ambiguity' would have been avoidable if template argument braces were not optional, right?

No, it would still be ambiguous:

struct S(T) {}

alias a = S!(int);

// Should this assertion pass or fail?
static assert(is(a));

February 02, 2020
On Sunday, 2 February 2020 at 16:23:42 UTC, Paul Backus wrote:

[..]
>
> No, it would still be ambiguous:
>
> struct S(T) {}
>
> alias a = S!(int);
>
> // Should this assertion pass or fail?
> static assert(is(a));

Sorry, I don't get it. AFAICS 'is(a)' should return true (since a is an alias for a full type here) - and braces being compulsory or optional does not affect this.

AFAICS:

struct S(T) {}

alias a = S!(int);
alias b = S;

// Should this assertion pass or fail?
static assert(is(a));  //PASS
static assert(is(b));  //FAIL

But I don't see how braces will affect this. Can you explain?
February 02, 2020
On 2/2/20 12:51 PM, ShadoLight wrote:

> // Should this assertion pass or fail?
> static assert(is(a));  //PASS
> static assert(is(b));  //FAIL
> 
> But I don't see how braces will affect this. Can you explain?

First, note that:

struct S(T) {}

is *exactly* equivalent to (in fact, you can write it this way, and it works exactly the same):

template S(T) { struct S {} }

So does S!int refer to the template instantiation (which is not a type) or the eponymous S struct inside the template (which is a type)? This was Paul's point. Since you didn't use any members, there is an ambiguity of intention (if accessing other parts of the template are allowed).

Even if you use a member, it's not hard to come up with an ambiguous example:

struct S(T) {
  template S(X) {}
}

What does S!int.S refer to? The S template inside the S struct, or the S struct itself inside the outer S template?

This is the main reason why eponymous templates were disallowed from using the classical template access syntax. I think this was not a mistake, and we are better off for it.

-Steve
February 02, 2020
On Sunday, 2 February 2020 at 18:30:17 UTC, Steven Schveighoffer wrote:

Thanks for taking the time to explain. However, when I tested my results does not seem to match your explanation.

>
> First, note that:
>
> struct S(T) {}
>
> is *exactly* equivalent to (in fact, you can write it this way, and it works exactly the same):
>
> template S(T) { struct S {} }

Yes, agreed.

>
> So does S!int refer to the template instantiation (which is not a type) or the eponymous S struct inside the template (which is a type)? This was Paul's point.

I get what you are trying to say, but testing actually shows S!int is already a type in the eponymous case (but not in the classical case). If I do as in Paul's example...

template S(T) {
    struct S {T x;}
}

// Should this assertion pass or fail?
static assert(is(S!(int)));   //PASS

void main()
{
    auto a = S!(int)(3);
    writeln(typeof(a).stringof);
}

... it actually compiles and the assertion passes (and prints S!int as the type), so it seems the eponymous template instantiation already created its eponymous struct as well, no?

But I guess that in terms of 'short-hand' syntax it actually makes sense since it is an eponymous template and, as you/Paul explained previously, it should only be invoked in this way.

In contrast, for the  non-eponymous case....

template R(T) {
    struct Q {T x;}
}

// Should this assertion pass or fail?
//static assert(is(R!(int)));  //FAIL

void main()
{
    auto b = R!(int).Q(3);
    writeln(typeof(b).stringof);	
}

... now the assertion fails as expected, but the type printed is simply Q.

So we actually have this:

template S(T) {
    struct S {}
}

template R(T) {
    struct Q {}
}

static assert(is(S!(int)) == true);   //PASS
static assert(is(R!(int)) == false);  //PASS

I can understand (as @MoonlightSentinel's example showed), that optional braces can make this ambiguous and that is the current situation.

But, my question was if this was avoidable if braces were not optional. Paul's answer was that non-optional braces will not make...
    alias a = S!(int);
... non-ambiguous, but I still don't get that based on the above results.

February 02, 2020
On Sunday, 2 February 2020 at 23:39:10 UTC, ShadoLight wrote:
> But, my question was if this was avoidable if braces were not optional. Paul's answer was that non-optional braces will not make...
>     alias a = S!(int);
> ... non-ambiguous, but I still don't get that based on the above results.

The results you've shown are based on the currently-implemented behavior, which is indeed unambiguous: S!(int) always refers to the eponymous struct, not the template instance that contains it. This is true whether or not you use braces.

If the behavior were changed as you have previously proposed, so that `S!(int)` could refer *either* to the eponymous struct *or* the template instance, then the alias declaration would become ambiguous. Again, this would be true whether or not you used braces.
February 03, 2020
On Sunday, 2 February 2020 at 23:48:45 UTC, Paul Backus wrote:
> On Sunday, 2 February 2020 at 23:39:10 UTC, ShadoLight wrote:
>> But, my question was if this was avoidable if braces were not optional. Paul's answer was that non-optional braces will not make...
>>     alias a = S!(int);
>> ... non-ambiguous, but I still don't get that based on the above results.

[..]

> If the behavior were changed as you have previously proposed, so that `S!(int)` could refer *either* to the eponymous struct *or* the template instance, then the alias declaration would become ambiguous. Again, this would be true whether or not you used braces.

Ok, I get it. Thanks.

1 2
Next ›   Last »