Jump to page: 1 2
Thread overview
Eponymous template with static if
Aug 09
sighoya
Aug 14
Tejas
Aug 15
sighoya
Aug 15
sighoya
Aug 15
sighoya
August 09

It's well known that template functions in D are just kind of eponymous template:

T square(T)(T t) {
    return t * t;
}

is lowered to

template square(T) {
    T square(T t) {
        return t * t;
    }
}

But if we write this:

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?

August 09
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
August 09

On Wednesday, 9 August 2023 at 16:55:04 UTC, Michael Galuza wrote:

>

But if we write this:

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

It seems not providing a template argument make it default to void.

August 10

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.

August 10

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:

>

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:

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.

August 10

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:

>

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:

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

August 14

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 that will do that for you(but I don't think it works when you explicitly use template keyword):

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:

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.

August 14

On Monday, 14 August 2023 at 03:18:17 UTC, Tejas wrote:

>

D has 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:

template square(T)
if (is(T)) // <-- right here
{
    T square(T param) { return param*param; }
}
August 15

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 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:

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.

August 15
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.
« First   ‹ Prev
1 2