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