vvvvvvvvvvvv TLDR vvvvvvvvvvvv

Eponymous templates that define a function pointer does not transfer the function call parameter to the enclosing template for type deduction. (Note: The term "deduction" is used with template parameters, not "inference".)

Also note that I am replacing 'enum' with 'auto' below to show this is not related to 'enum' vs 'auto'.

template foo (T) {
    auto foo = (T i) => i >> 1;
}

void main() {
    foo(42); // <-- Compilation ERROR
}

Error: none of the overloads of template `deneme.foo` are callable using argument types `!()(int)`
       Candidate is: `foo(T)`

I am not sure whether this limitation is a bug.

^^^^^^^^^^^^ TLDR ^^^^^^^^^^^^

On 1/11/23 10:01, Salih Dincer wrote:

> void main()
> {
>      enum foo : char { a = 'H', b = 'i' }

That defines two values: foo.a is 'H' and foo.b is 'i'.

>      enum bar() { return new foo; }

That is a function that returns a dynamically allocated foo value. Since 'new' makes pointers for value types like int and enum, I think the return type of 'bar' is foo*.

And that pointer is pointing at a foo.init, which happens to be the first value defined: 'H'.

>
>      import std.stdio;
>      foreach(char f; [bar.a, bar.b])

'bar' is a function call that returned a pointer. Hm. Finally I see what you are talking about.

Normally, one might have written (*bar) to get the value. Let's try by using the following array:

  [(*bar), (*bar)]

Ok, it's what I expect:

HH

Now the interesting part is what does (*bar).a and (*bar).b mean? Let's try:

  [(*bar).a, (*bar).b]

Ok, it gives

Hi

The interesting thing is, foo.a does not print 'H' but 'a':

    writeln(foo.a);
    writeln(foo.b);

a
b

Ah! Finally I see your explicit 'char' in the foreach loop. That is what makes your code print 'H' and 'i'. :)

>          f.write;
>
>      writeln; // Hi
> }

I've just learned something: You can reach different enum values directly from an value itself:

import std;

void main()
{
    // A type:
    enum foo : char { a = 'H', b = 'i' }

    // A variable:
    enum x = foo.a;

    // Really?
    writeln(x.b);
}

Ok, it kind of makes sense but I would have written the following e.g. in template code. (?)

    writeln(typeof(x).b);

> I think this use of enums should be prohibited. So, can I get an answer
> about not being able to make a type inference? So I'm very curious about
> the second part of the my post.

Before looking at your example, here is what I tested. The code proves that type inference is the same for 'auto' and 'enum' functions:

auto foo(T)(T t) {
    return t;
}

enum bar(T)(T t) {
    return t;
}

struct S {}

import std.meta;

void main() {
    alias functions = AliasSeq!(foo, bar);
    alias types = AliasSeq!(int, double, S, string, const(float));

    static foreach (type; types) {
        pragma(msg, "Testing ", type);
        static foreach (func; functions) {
            static assert (is (typeof(func(type.init)) == type));
        }
    }
}

It uses two function templates and a few types and as expected, the return types of the functions are the same.

Ok, back to your code:

> template foo (T) {
>     enum foo = (T i) => i >> 1;
> }

That local 'foo' is a literal value of an anonymous function. (The local 'foo' is not a template.) Reminding myself: Literals are rvalues and they are not variables. For example, they don't have addresses.

> template bar (T) {
>     auto bar (T i) => i >> 1;
> }

That's different: There was an equals sign after 'foo' but not here. So your example is different from e.g. the following two definitions:

auto x() {}
enum y() {}

If you are curious about this case, as Adam said, there is no difference between 'auto' and 'enum'. But again, your code has that extra '=' character for 'foo' and that makes a difference.

Getting back to 'bar', so that local 'bar' is a function definition. Now, calling bar from the outside as bar(42) is the same as writing bar!int.bar(42). (This is the eponymous template convenience.)

> import std;

> void main()
> {
>     assert(oneHalf(42) == 21);
>     42.foo!int.writeln; // no inference: "21"

Your "no inference" comment was confusing me. Now I see that you meant that you can't write the following:

  42.foo.writeln; // <-- Compilation ERROR

Although you can write the following:

>     42.bar.writeln; // "21"
> }

Ok, let's look at the problem with the understanding of 'foo' being a literal. Yes, I can demonstrate the issue that mix it with the concept of type inference:

import std;

void main() {
    enum foo = {           // Note '='
        writeln("foo");
    };

    auto bar() {
        writeln("bar");
    }

    foo;  // <-- Compilation ERROR
    bar;
}

foo line has a compilation error:

Error: `__lambda1` has no effect

Because it's a function pointer, one needs to call it with parenthesis:

    foo();
    bar;

Now it works.

The reason why 'bar' does not need the parenthesis is because functions have that famous convenience in D: When the parameter list is empty, the parenthesis are optional. Apparently, this is not the case for literals. There is a good reason for that: You wouldn't want 'foo' be a function call every time.

For example, we might want the function pointer value of 'foo', not the result of calling it:

  auto anotherFunctionPointer = foo;

But then you would argue that your 'foo' does have an 'int' parameter. Why wouldn't the enclosing template parameter deduced for it? I agree. I put this issue at the top as TLDR.

Ali

Reply via email to