www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Eponymous template with static if

reply Michael Galuza <riddlermichael gmail.com> writes:
It's well known that template functions in D are just kind of 
eponymous template:
```d
T square(T)(T t) {
     return t * t;
}
```
is lowered to
```d
template square(T) {
     T square(T t) {
         return t * t;
     }
}
```

But if we write this:
```d
template square(T) {
     static if (true) {
         T square(T t) {
             return t * t;
         }
     }
}
}
```
trivial expression `square(3)` won't compiled with message
`Error: none of the overloads of template main.square are 
callable using argument types !()(int)`.

Why? What I missed?
Aug 09 2023
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/9/23 12:55 PM, Michael Galuza wrote:
 It's well known that template functions in D are just kind of eponymous 
 template:
 ```d
 T square(T)(T t) {
      return t * t;
 }
 ```
 is lowered to
 ```d
 template square(T) {
      T square(T t) {
          return t * t;
      }
 }
 ```
 
 But if we write this:
 ```d
 template square(T) {
      static if (true) {
          T square(T t) {
              return t * t;
          }
      }
 }
 }
 ```
 trivial expression `square(3)` won't compiled with message
 `Error: none of the overloads of template main.square are callable using 
 argument types !()(int)`.
 
 Why? What I missed?
In order for IFTI (Implicit Function Template Instantiation) to work, template functions must be of a certain form. If I recall correctly, they must be immediate children of the template AST node. You can stuff other things in the template, but you can't do something like what you did. The fact that IFTI works at all is kind of a kludge. The compiler must match the argument types to the function call nested in the template to do pattern matching, and *then* instantiate the template. In other words, it has to look inside before instantiation. -Steve
Aug 09 2023
prev sibling next sibling parent reply sighoya <sighoya gmail.com> writes:
On Wednesday, 9 August 2023 at 16:55:04 UTC, Michael Galuza wrote:

 But if we write this:
 ```d
 template square(T) {
     static if (true) {
         T square(T t) {
             return t * t;
         }
     }
 }
 }
 ```
 trivial expression `square(3)` won't compiled with message
 `Error: none of the overloads of template main.square are 
 callable using argument types !()(int)`.

 Why? What I missed?
Giving it an explicit type make it workable [example](https://run.dlang.io/?compiler=dmd&source=import%20std.stdio;%0A%0Atemplate%20square(T)%20%7B%0A%20%20%20%20static%20if%20(true)%20%7B%0A%20%20%20%20%20%20%20%20T%20square(T%20t)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20t%20*%20t;%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%0A%0Avoid%20main()%0A%7B%0A%20%20%20%20%2F%2F%20Let%27s%20get%20going!%0A%20%20%20square!(int)(3);%0A%20%20%20writeln(square!(int)(3));%0A%20%20%20%20%2F%2F%20%22Range%20algorithms%22%20page%20under%20%22Gems%22%0A%7D) It seems not providing a template argument make it default to void.
Aug 09 2023
parent reply Michael Galuza <riddlermichael gmail.com> writes:
On Wednesday, 9 August 2023 at 18:28:38 UTC, sighoya wrote:
 Giving it an explicit type make it workable
Of course, but such usage of a template functions is... useless:-) I want type argument `T` will be inferred. Anyway, I wondered why compiler rewrite `square!(int)(3)` as `square!(int).square(3)`, but in the same time it's not so clever to eliminate `!(int)`. I can only guess that type inference is impossible in this situation. I mean in ordinary cases compiler can match literal `3` and type `int` directly, but what it should do in such simple case: ``` template square(T) { static if (is(T)) { T square(T t) { return t * t; } } } ``` Of course compiler could conclude that expression `square(3)` is correct only if `static if` branch was successful, so `is(T) == true && T == int` (hi, Hindley–Milner :-), but such type inference require complex analysis "bottom-up", from template members to template itself, whereas we analyse template body "up-down", from template args and static if-s to members. Of course all above is just my poor and miserable tryings to understand compiler's pranks, don't judge me.
Aug 10 2023
parent reply Tejas <notrealemail gmail.com> writes:
On Thursday, 10 August 2023 at 14:34:20 UTC, Michael Galuza wrote:
 On Wednesday, 9 August 2023 at 18:28:38 UTC, sighoya wrote:
 Giving it an explicit type make it workable
Of course, but such usage of a template functions is... useless:-) I want type argument `T` will be inferred. Anyway, I wondered why compiler rewrite `square!(int)(3)` as `square!(int).square(3)`, but in the same time it's not so clever to eliminate `!(int)`. I can only guess that type inference is impossible in this situation. I mean in ordinary cases compiler can match literal `3` and type `int` directly, but what it should do in such simple case: ``` template square(T) { static if (is(T)) { T square(T t) { return t * t; } } } ``` Of course compiler could conclude that expression `square(3)` is correct only if `static if` branch was successful, so `is(T) == true && T == int` (hi, Hindley–Milner :-), but such type inference require complex analysis "bottom-up", from template members to template itself, whereas we analyse template body "up-down", from template args and static if-s to members. Of course all above is just my poor and miserable tryings to understand compiler's pranks, don't judge me.
D has [template constraints](https://dlang.org/spec/template.html#template_constraints) that will do that for you(but I don't think it works when you explicitly use `template` keyword): ```d import std; T square(T)(T param)if(is(T == int)){ return param * param; } void main() { writeln(square(123)); } ``` And if you want to use templates explicitly, you can use template specialization: ```d import std; template square(T:int){ T square(T param){ return param * param; } } void main() { writeln(square(123)); } ``` And, as you mentioned already, getting the compiler to deduce the kind of types that are valid for a given template body is not possible right now.
Aug 13 2023
parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 14 August 2023 at 03:18:17 UTC, Tejas wrote:
 D has [template 
 constraints](https://dlang.org/spec/template.html#template_constraints) that
will do that for you(but I don't think it works when you explicitly use
`template` keyword):
You can use constraints with the `template` keyword. They attach to the template itself, not the function: ```d template square(T) if (is(T)) // <-- right here { T square(T param) { return param*param; } } ```
Aug 14 2023
parent reply sighoya <sighoya gmail.com> writes:
On Monday, 14 August 2023 at 14:49:06 UTC, Paul Backus wrote:
 On Monday, 14 August 2023 at 03:18:17 UTC, Tejas wrote:
 D has [template 
 constraints](https://dlang.org/spec/template.html#template_constraints) that
will do that for you(but I don't think it works when you explicitly use
`template` keyword):
You can use constraints with the `template` keyword. They attach to the template itself, not the function: ```d template square(T) if (is(T)) // <-- right here { T square(T param) { return param*param; } } ```
Can you tell what `is(T)` is doing here? From the docs it seems to require `T` to be existing. However, I don't know what this exactly means, as T anyway have to exist.
Aug 15 2023
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 8/15/23 12:25, sighoya wrote:
 On Monday, 14 August 2023 at 14:49:06 UTC, Paul Backus wrote:
 On Monday, 14 August 2023 at 03:18:17 UTC, Tejas wrote:
 D has [template 
 constraints](https://dlang.org/spec/template.html#template_constraints) that
will do that for you(but I don't think it works when you explicitly use
`template` keyword):
You can use constraints with the `template` keyword. They attach to the template itself, not the function: ```d template square(T) if (is(T)) // <-- right here {     T square(T param) { return param*param; } } ```
Can you tell what `is(T)` is doing here?
Here it indeed serves no particular purpose, I think this was just to illustrate the syntax.
  From the docs it seems to require `T` to be existing.
 However, I don't know what this exactly means, as T anyway have to exist.
It checks whether `T` is a valid type. I think currently nobody knows what this means, but there is a reference implementation.
Aug 15 2023
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, Aug 15, 2023 at 10:25:56AM +0000, sighoya via Digitalmars-d wrote:
 On Monday, 14 August 2023 at 14:49:06 UTC, Paul Backus wrote:
[...]
 ```d
 template square(T)
 if (is(T)) // <-- right here
 {
     T square(T param) { return param*param; }
 }
 ```
Can you tell what `is(T)` is doing here? From the docs it seems to require `T` to be existing. However, I don't know what this exactly means, as T anyway have to exist.
`is(T)` is standard idiom for checking whether T is a valid type. It's usually used with an expression to test whether some operation is valid on a type, e.g.: auto myTemplateFunc(T)(T data) if (is(data++)) // make sure ++ is valid on T { return data++; } auto myTemplateFunc(T)(T data) if (is(data+0)) // make sure + int is valid on T { return data + 123; } T -- English is useful because it is a mess. Since English is a mess, it maps well onto the problem space, which is also a mess, which we call reality. Similarly, Perl was designed to be a mess, though in the nicest of all possible ways. -- Larry Wall
Aug 15 2023
next sibling parent Nick Treleaven <nick geany.org> writes:
On Tuesday, 15 August 2023 at 10:54:52 UTC, H. S. Teoh wrote:
  `is(T)` is standard idiom for checking whether T is a valid
 type.

 It's usually used with an expression to test whether some 
 operation is valid on a type, e.g.:

 	auto myTemplateFunc(T)(T data)
 		if (is(data++))	// make sure ++ is valid on T
 	{
 		return data++;
 	}

 	auto myTemplateFunc(T)(T data)
 		if (is(data+0))	// make sure + int is valid on T
 	{
 		return data + 123;
 	}
Those should be `is(typeof(data++))` and `is(typeof(data+0))`.
Aug 15 2023
prev sibling parent reply sighoya <sighoya gmail.com> writes:
On Tuesday, 15 August 2023 at 10:54:52 UTC, H. S. Teoh wrote:
 `is(T)` is standard idiom for checking whether T is a valid 
 type.
Thanks. Though, is there any case where it wouldn't be a valid type?
Aug 15 2023
next sibling parent sighoya <sighoya gmail.com> writes:
On Tuesday, 15 August 2023 at 21:24:34 UTC, sighoya wrote:
 On Tuesday, 15 August 2023 at 10:54:52 UTC, H. S. Teoh wrote:
 `is(T)` is standard idiom for checking whether T is a valid 
 type.
Thanks. Though, is there any case where it wouldn't be a valid type?
As template parameter types seem to be distinguishable by syntax in my eyes: https://dlang.org/spec/template.html#parameters
Aug 15 2023
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Tuesday, 15 August 2023 at 21:24:34 UTC, sighoya wrote:
 On Tuesday, 15 August 2023 at 10:54:52 UTC, H. S. Teoh wrote:
 `is(T)` is standard idiom for checking whether T is a valid 
 type.
Thanks. Though, is there any case where it wouldn't be a valid type?
See: https://dlang.org/spec/expression.html#is-type
Aug 16 2023
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 15 August 2023 at 10:25:56 UTC, sighoya wrote:
 Can you tell what `is(T)` is doing here?
 From the docs it seems to require `T` to be existing.
 However, I don't know what this exactly means, as T anyway have 
 to exist.
It doesn't do anything. It's only there because I copied it from Michael Galuza's post (where it also doesn't do anything): https://forum.dlang.org/post/qglavaqzfzzxjcrfufpw forum.dlang.org
Aug 15 2023
prev sibling parent reply Michael Galuza <riddlermichael gmail.com> writes:
On Wednesday, 9 August 2023 at 16:55:04 UTC, Michael Galuza wrote:
 Why? What I missed?
Oh, I'm so stupid :-( `static if` restriction described in [26.1.1.1.2](https://dlang.org/spec/template.html#ifti-restrictions):
 When the template parameters must be deduced, the eponymous 
 members can't rely on a `static if` condition since the 
 deduction relies on how the members are used:
```d template foo(T) { static if (is(T)) // T is not yet known... void foo(T t) {} // T is deduced from the member usage } void main() { //foo(0); // Error: cannot deduce function from argument types foo!int(0); // Ok since no deduction necessary } ``` I bet that this paragraph was added immediately after my question.
Aug 10 2023
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/10/23 12:44 PM, Michael Galuza wrote:
 On Wednesday, 9 August 2023 at 16:55:04 UTC, Michael Galuza wrote:
 Why? What I missed?
Oh, I'm so stupid :-( `static if` restriction described in [26.1.1.1.2](https://dlang.org/spec/template.html#ifti-restrictions):
 When the template parameters must be deduced, the eponymous members 
 can't rely on a `static if` condition since the deduction relies on 
 how the members are used:
```d template foo(T) {     static if (is(T)) // T is not yet known...         void foo(T t) {} // T is deduced from the member usage } void main() {     //foo(0); // Error: cannot deduce function from argument types     foo!int(0); // Ok since no deduction necessary } ```
Oh nice! I wish I had known this was in there I would have just pointed at it.
 
 I bet that this paragraph was added immediately after my question.
The site is on github ;) https://github.com/dlang/dlang.org/commit/190da42b6d6fe0abc205fda66a1d0e4086c5cf5c -Steve
Aug 10 2023