www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - enum functions

reply Salih Dincer <salihdb hotmail.com> writes:
Aho,

I accidentally had a function like this:

```d
     enum oneHalf(T)(T i) {
         return i >> 1;
     }
```

I'm wondering 2 things; firstly, does having an enum mean there 
is no auto-return? Or could it be CTFE?

Second, why is there no inference when I use an enum in one of 
the functions below?

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

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

import std;

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

Thanks...

SDB 79
Jan 08 2023
next sibling parent Salih Dincer <salihdb hotmail.com> writes:
On Sunday, 8 January 2023 at 18:42:58 UTC, Salih Dincer wrote:
 I accidentally had a function like this:

 ```d
     enum oneHalf(T)(T i) {
         return i >> 1;
     }
 ```
Also it seemed odd to me that this could compile! SDB 79
Jan 08 2023
prev sibling next sibling parent Salih Dincer <salihdb hotmail.com> writes:
On Sunday, 8 January 2023 at 18:42:58 UTC, Salih Dincer wrote:
 
 I'm wondering 2 things; firstly, does having an enum mean there 
 is no auto-return? Or could it be CTFE?

 Second, why is there no inference when I use an enum in one of 
 the functions below?
I'm still wondering 😀 SDB 79
Jan 09 2023
prev sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Sunday, 8 January 2023 at 18:42:58 UTC, Salih Dincer wrote:
 I'm wondering 2 things; firstly, does having an enum mean there 
 is no auto-return? Or could it be CTFE?
It means nothing. The keyword tells the parser a function is about to begin, which triggers return type inference (exactly the same as `auto`), but otherwise it is completely ignored.
Jan 10 2023
next sibling parent bauss <jacobbauss gmail.com> writes:
On Tuesday, 10 January 2023 at 12:55:34 UTC, Adam D Ruppe wrote:
 On Sunday, 8 January 2023 at 18:42:58 UTC, Salih Dincer wrote:
 I'm wondering 2 things; firstly, does having an enum mean 
 there is no auto-return? Or could it be CTFE?
It means nothing. The keyword tells the parser a function is about to begin, which triggers return type inference (exactly the same as `auto`), but otherwise it is completely ignored.
I'm curious about why it is allowed and whether we somehow could use it for something meaningful since it really just is an alias for auto right now. Perhaps it could be used to create functions that only live at compile-time.
Jan 11 2023
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/10/23 7:55 AM, Adam D Ruppe wrote:
 On Sunday, 8 January 2023 at 18:42:58 UTC, Salih Dincer wrote:
 I'm wondering 2 things; firstly, does having an enum mean there is no 
 auto-return? Or could it be CTFE?
It means nothing. The keyword tells the parser a function is about to begin, which triggers return type inference (exactly the same as `auto`), but otherwise it is completely ignored.
Mostly correct. `enum` is a storage class, which means a *declaration* is about to begin. Once the declaration is decided as a function definition, type inference is performed, and the storage class is checked to see how it affects the declaration. In some cases, there is an effect, in some there is not. ```d static foo() {} // static does nothing here final bar() {} // final does nothing here enum baz() {} // enum does nothing here __gshared fun() {} // __gshared does nothing here struct S { // return type is int *, `this` (unused) is const const foo() { return new int; } // same for immutable, shared, inout } ``` Aside from the const/immutable/shared/inout storage classes (which do affect semantics, but maybe in slightly confusing ways) there are some which are pretty obvious and also work with type inference: 1. deprecated 2. extern 3. abstract 4. override 5. synchronized 6. auto 7. scope 8. nothrow 9. pure 10. ref -Steve
Jan 11 2023
prev sibling parent reply Salih Dincer <salihdb hotmail.com> writes:
On Tuesday, 10 January 2023 at 12:55:34 UTC, Adam D Ruppe wrote:
 It means nothing. The keyword tells the parser a function is 
 about to begin, which triggers return type inference (exactly 
 the same as `auto`), but otherwise it is completely ignored.
Sorry to give compelling examples but it can be pretty confusing if it comes across as if by mistake, am I wrong? ```d void main() {    enum foo : char { a = 'H', b = 'i' }    enum bar() { return new foo; } import std.stdio;    foreach(char f; [bar.a, bar.b]) f.write;    writeln; // Hi } ``` 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. Thanks... SDN 79
Jan 11 2023
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Jan 11 2023