Re: Template Usage with Eponymous Trick

2020-02-02 Thread ShadoLight via Digitalmars-d-learn

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.



Re: Template Usage with Eponymous Trick

2020-02-02 Thread Paul Backus via Digitalmars-d-learn

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.


Re: Template Usage with Eponymous Trick

2020-02-02 Thread ShadoLight via Digitalmars-d-learn
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.




Re: Template Usage with Eponymous Trick

2020-02-02 Thread Steven Schveighoffer via Digitalmars-d-learn

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


Re: Template Usage with Eponymous Trick

2020-02-02 Thread ShadoLight via Digitalmars-d-learn

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?


Re: Template Usage with Eponymous Trick

2020-02-02 Thread Paul Backus via Digitalmars-d-learn

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));



Re: Template Usage with Eponymous Trick

2020-02-02 Thread ShadoLight via Digitalmars-d-learn
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.




Re: Template Usage with Eponymous Trick

2020-01-31 Thread Steven Schveighoffer via Digitalmars-d-learn

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


Re: Template Usage with Eponymous Trick

2020-01-30 Thread ShadoLight via Digitalmars-d-learn

On Thursday, 30 January 2020 at 20:42:02 UTC, Paul Backus wrote:

On Thursday, 30 January 2020 at 15:14:43 UTC, ShadoLight wrote:



[..]

would make the language grammatically ambiguous...


OK, understood. Thank you.




Re: Template Usage with Eponymous Trick

2020-01-30 Thread ShadoLight via Digitalmars-d-learn
On Thursday, 30 January 2020 at 20:00:05 UTC, MoonlightSentinel 
wrote:

On Thursday, 30 January 2020 at 17:00:08 UTC, ShadoLight wrote:

[..]

...your proposed change it would be ambigous ...


Ok, that makes sense - I did not consider the impact of the 
optional empty braces. Thank you.




Re: Template Usage with Eponymous Trick

2020-01-30 Thread Paul Backus via Digitalmars-d-learn

On Thursday, 30 January 2020 at 15:14:43 UTC, ShadoLight wrote:


Is there a technical reason for this limitation? Why are the 
'classical' invocation style not allowed for eponymous 
templates as well?


The 'classical' invocation style is not allowed because it would 
make the language grammatically ambiguous. Without semantic 
analysis, the compiler would have no way of knowing whether 
`foo!(int, int)` was intended to refer to the template instance 
foo!(int, int), or the eponymous member foo!(int, int).foo.


Re: Template Usage with Eponymous Trick

2020-01-30 Thread MoonlightSentinel via Digitalmars-d-learn

On Thursday, 30 January 2020 at 17:00:08 UTC, ShadoLight wrote:
Agreed. My question though is should the 'shorthand' notation 
_replace_ the 'longhand' notation, or be available _in 
addition_ to the 'longhand' notation in the eponymous case (so 
the eponymous notation is just 'syntax sugar' if you will).




Consider the following example:

T foo(T = int)(T val = T.init)
{
   return val + 1;
}

void main()
{
auto i = foo!().foo();
writeln(i);
}

What should be the value of i?
1: foo!() is the template instantiation, foo() the method call
2: foo!() and foo() are method calls.

The answer is 2 (because empty braces are optional) but with your 
proposed change it would be ambigous (unless one interpreation 
was prioritized which would probably be more confusing).


Maybe we should rather ask what benefit results from allowing 
this explicit notation? IMHO the entire purpose of epynemous 
templates is to make templates containing one public* symbol less 
verbose which is a common use case.


*public ~ useful outside of the templated symbol

And, in that case some of the examples in the documentation 
needs fixing.


Agreed, the documentation could use some polishing.


Re: Template Usage with Eponymous Trick

2020-01-30 Thread ShadoLight via Digitalmars-d-learn
On Thursday, 30 January 2020 at 16:16:48 UTC, MoonlightSentinel 
wrote:

On Thursday, 30 January 2020 at 15:14:43 UTC, ShadoLight wrote:


[...]


From my POV is

void foo(T)() { ... }

just a shorthand notation for...



Agreed. My question though is should the 'shorthand' notation 
_replace_ the 'longhand' notation, or be available _in addition_ 
to the 'longhand' notation in the eponymous case (so the 
eponymous notation is just 'syntax sugar' if you will).


If you had...

template foo(T) {
   bar(){..}
}

...you have no choice but to use foo!(int).bar()- (where T is 
'int'). So, I'm asking, in the eponymous case, should...


template foo(T) {
   foo(){..}
}

force you to use foo!(int)() instead (as is currently the case), 
or should foo!(int).foo() also still be acceptable/available?


For consistency's sake I think it should be but, if there is some 
reason why this is not technically possible/advisable, I was 
hoping someone would enlighten me.


And, in that case some of the examples in the documentation needs 
fixing.




Re: Template Usage with Eponymous Trick

2020-01-30 Thread MoonlightSentinel via Digitalmars-d-learn

On Thursday, 30 January 2020 at 15:14:43 UTC, ShadoLight wrote:
Is there a technical reason for this limitation? Why are the 
'classical' invocation style not allowed for eponymous 
templates as well?


It seems somewhat arbitrary - note that inner/outer functions 
does not have this limitation - the fllowing is legal and 
compiles (and does not lead to infinite recursion):


I guess the intention is to e.g. allow UFCS on the return values 
of templated functions without ambiguities, e.g.:


void main()
{
import std.algorithm, std.stdio;
int[] values = [ 1, 2, 3, 4 ];

values.filter!(i => i % 2 == 0)
  .map!(i => i / 2) // Does not refer to any hidden 
member of template filter

  .each!writeln;
}

From my POV is

void foo(T)() { ... }

just a shorthand notation for

template foo(T)
{
void foo() {}
}

allthough it could probably use some improvements to the 
documentation.


Re: Template Usage with Eponymous Trick

2020-01-30 Thread ShadoLight via Digitalmars-d-learn

On Thursday, 30 January 2020 at 14:22:11 UTC, Paul Backus wrote:

On Thursday, 30 January 2020 at 14:10:38 UTC, ShadoLight wrote:
...but in the 'classical' default template style, so I would 
have thought the 
template_name!(compile_time_args).function_name(run_time_args) 
style would still work, even if the template and function 
names are identical.


If this is in fact now the intended behavior, then there are 
some places where the documentation are in error.



[1]: https://dlang.org/spec/template.html#variadic-templates


Eponymous templates are documented here:

https://dlang.org/spec/template.html#implicit_template_properties

This specific behavior is documented in the first paragraph 
below that heading, which reads as follows:


If a template contains members whose name is the same as the 
template identifier and if the type or the parameters type of 
these members include at least all the template parameters 
then these members are assumed to be referred to in a template 
instantiation


Yes, the behavior is described but not that it is in fact the 
_only_ way that an eponymous template can in fact be instantiated.


I think this behavior will be surprising for someone new to D. 
For eponymous templates you actually have the short-hand 
declarative style as well...


void foo(S, T)(S s, T t) {}

..ok, needs to call with...
foo!(int, int) (1, 2);
...or even just...
foo(1, 2);
...and your template parameters will be deduced.

This is at least kind-of intuitive. But now if you declare the 
template in the fully standard way:


   template foo(S, T)
   {
   void foo(S s, T t) {}
   }

..and you call it in the completely standard way (as you would 
have in fact been required if template identifier and member were 
differently named)...

   foo!(int, int).foo(1, 2);
..it not only does _not_ compile, but gives you an error:

onlineapp.d(12): Error: function onlineapp.foo!(int, int).foo(int 
s, int t) is not callable using argument types ()


...where foo!(int, int).foo(int s, int t) is clearly a match!

I really like the eponymous template trick and all that, but this 
did catch me by surprise -I did not realize that the eponymous 
template style in fact invalidates the 'classic' template 
invocation style: it is one or the other. I was somehow under the 
mistaken impression that you could still revert to the classic 
style, even if your template identifier and member identifier 
were identical.


Is there a technical reason for this limitation? Why are the 
'classical' invocation style not allowed for eponymous templates 
as well?


It seems somewhat arbitrary - note that inner/outer functions 
does not have this limitation - the fllowing is legal and 
compiles (and does not lead to infinite recursion):


int foo(int a, int b)
{
int foo(int x, int y) {return x+y;}
return foo(a, b);
}


void main()
{
   int z = foo(2, 4);
}



Re: Template Usage with Eponymous Trick

2020-01-30 Thread Paul Backus via Digitalmars-d-learn

On Thursday, 30 January 2020 at 14:10:38 UTC, ShadoLight wrote:
...but in the 'classical' default template style, so I would 
have thought the 
template_name!(compile_time_args).function_name(run_time_args) 
style would still work, even if the template and function names 
are identical.


If this is in fact now the intended behavior, then there are 
some places where the documentation are in error.



[1]: https://dlang.org/spec/template.html#variadic-templates


Eponymous templates are documented here:

https://dlang.org/spec/template.html#implicit_template_properties

This specific behavior is documented in the first paragraph below 
that heading, which reads as follows:


If a template contains members whose name is the same as the 
template identifier and if the type or the parameters type of 
these members include at least all the template parameters then 
these members are assumed to be referred to in a template 
instantiation


Re: Template Usage with Eponymous Trick

2020-01-30 Thread ShadoLight via Digitalmars-d-learn

On Thursday, 30 January 2020 at 14:10:38 UTC, ShadoLight wrote:
Taking this example from documentation page on 'Template 
Sequence Parameters' [1]:


[...]


Tested on https://run.dlang.io