Thread overview
Poll: what should this program do?
Mar 19
monkyyy
Mar 19
Ogion
Mar 19
Dukc
Mar 19
Dukc
Mar 20
Basile B.
March 19

Here's a weird little program that came up in a recent Discord discussion:

template fun(T)
{
    string fun(T) => fun("hi");
    string fun(string) => "inner";
}
string fun(string) => "outer";

void main()
{
    import std.stdio;
    writeln(fun(123));
}

What SHOULD this program do?

  1. Print "inner".
  2. Print "outer".
  3. It shouldn't compile.

What do you think it ACTUALLY does?

  1. Print "inner".
  2. Print "outer".
  3. It doesn't compile.
March 19

On Wednesday, 19 March 2025 at 03:40:28 UTC, Paul Backus wrote:

>

Here's a weird little program that came up in a recent Discord discussion:

template fun(T)
{
    string fun(T) => fun("hi");
    string fun(string) => "inner";
}
string fun(string) => "outer";

void main()
{
    import std.stdio;
    writeln(fun(123));
}

What SHOULD this program do?

  1. Print "inner".
  2. Print "outer".
  3. It shouldn't compile.

What do you think it ACTUALLY does?

  1. Print "inner".
  2. Print "outer".
  3. It doesn't compile.

It should print outer and does print outer
you didnt actually introduce ambiguity

template fun(alias T)
{
    string fun(T) => fun("hi");
    string fun(string) => "inner";
}
string fun(string) => "outer";

void main()
{
    import std.stdio;
    writeln(fun(123));
}

fails

March 19

On Wednesday, 19 March 2025 at 03:40:28 UTC, Paul Backus wrote:

>

What SHOULD this program do?

What do you think it ACTUALLY does?

It should print “outer”, and it actually does.

Obviously, at first I expected it to print: “inner”. But when I saw the result and checked the spec, I think that it works correctly.

There are three non-obvious things ging in here.

Number one: fun(T).fun(string) is not a regular function, it’s a template. So it doesn’t collide with globally-defined fun(string). This is why this program compiles.

Number two: inside eponymous template fun, fun refers to the template itself, not to its members. This gives a different result:

template fun(T) {
    alias fun = f;
    string f(int) => f("hi");
    string f(string) => "inner";
}
string f(string) => "outer";

void main() {
    import std.stdio;
    writeln(fun!int(123)); // sorry, IFTI no longer works
}

Number three: using a type instead of template parameter (of the same type) is enough to break implicit instantiation:

template fun(T) {
    string fun(string) => "inner";
}

void main() {
    import std.stdio;
    writeln(fun("hi")); // error
    writeln(fun!string("hi")); // works
}

There are many other ways to break IFTI:

template fun(T) {
    alias fun = fun;
    string f(T) => "inner";
}
template fun(T) {
    template fun(T) {
        string fun(T) => "inner";
    }
}

I guess it can’t be helped. Usually, though, IFTI failure results in error. In this program, it leads to unforeseen behavior. But such is life on templates.

March 19

On Wednesday, 19 March 2025 at 03:40:28 UTC, Paul Backus wrote:

>

Here's a weird little program that came up in a recent Discord discussion:

template fun(T)
{
    string fun(T) => fun("hi");
    string fun(string) => "inner";
}
string fun(string) => "outer";

void main()
{
    import std.stdio;
    writeln(fun(123));
}

What SHOULD this program do?

Print "inner". Local symbol should hide the module-scope symbol, and "inner" printer is local from where fun("hi") is called. The spec says that function overloading rules should only apply to functions in the same scope.

>

What do you think it ACTUALLY does?

No idea. What I think it ought to do would be my expectation but the fact you posted this as a riddle hints it maybe doesn't work like that.

March 19

On Wednesday, 19 March 2025 at 10:31:36 UTC, Ogion wrote:

>

Number two: inside eponymous template fun, fun refers to the template itself, not to its members. This gives a different result:

Well duh, I see how this causes the program to print "outer", but is this actually how it should be? If, why?

March 19

On Wednesday, 19 March 2025 at 10:31:36 UTC, Ogion wrote:

>

Number two: inside eponymous template fun, fun refers to the template itself, not to its members.

Sometimes...but not all the time. Check this out:

template fun(T)
{
    // call via function pointer
    string fun(T) => (&fun)("hi");
    string fun(string) => "inner";
}
string fun(string) => "outer";

void main()
{
    import std.stdio;
    writeln(fun(123));
}
March 20

On Wednesday, 19 March 2025 at 10:31:36 UTC, Ogion wrote:

>

Number two: inside eponymous template fun, fun refers to the template itself, not to its members.

That's what I don't like. I'd prefer it to refer to the members of the template instance unless qualified.

March 20

On Wednesday, 19 March 2025 at 03:40:28 UTC, Paul Backus wrote:

>

Here's a weird little program that came up in a recent Discord discussion:

template fun(T)
{
    string fun(T) => fun("hi");
    string fun(string) => "inner";
}
string fun(string) => "outer";

void main()
{
    import std.stdio;
    writeln(fun(123));
}

What an overload-set nightmare.

>

What SHOULD this program do?

  1. Print "inner".
  2. Print "outer".
  3. It shouldn't compile.

3 because two candidates of the set match.

>

What do you think it ACTUALLY does?

  1. Print "inner".
  2. Print "outer".
  3. It doesn't compile.

I play the game, I haven't compiled the thing but I'd say "hi".

March 20
On 3/19/25 04:40, Paul Backus wrote:
> Here's a weird little program that came up in a recent Discord discussion:
> 
>      template fun(T)
>      {
>          string fun(T) => fun("hi");
>          string fun(string) => "inner";
>      }
>      string fun(string) => "outer";
> 
>      void main()
>      {
>          import std.stdio;
>          writeln(fun(123));
>      }
> 
> 
> **What SHOULD this program do?**
> 
> 1. Print "inner".
> 2. Print "outer".
> 3. It shouldn't compile.
> ...

According to the documentation, IFTI wouldn't really apply at all because the first declaration is not a function template. Of course, what happens in practice is different. (But with the same result here.)


> **What do you think it ACTUALLY does?**
> 
> 1. Print "inner".
> 2. Print "outer".
> 3. It doesn't compile.

Maybe "stack overflow' should have been one of the options ;).

It prints "outer", because IFTI is considered a worse match due to `T` being deduced. I don't think this behavior is documented. (But if you do `template foo()` and replace `T` with `string` in the body, you will get an ambiguity error.)

This is another fun case:

```d
template fun(T)
{
    string fun(string) => "first";
    string fun(const(T)) => fun("second");
}
pragma(msg, fun("123"));
```

```d
template fun(T=string)
{
	string fun(string) => "first";
	string fun(const(T)) => "second";
}
pragma(msg, fun("123"));
```