www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Feedback Thread: DIP 1044--Enum Type Inference--Community Review Round

reply Mike Parker <aldacron gmail.com> writes:


This is the feedback thread for the first round of Community 
Review of DIP 1044, "Enum Type Inference".

**THIS IS NOT A DISCUSSION THREAD**

I will be deleting posts that do not follow the Feedback Thread 
rules outlined at the following link:

https://github.com/dlang/DIPs/blob/master/docs/guidelines-reviewers.md

The rules are also repeated below. Recently, I have avoided 
deleting posts that violate the rules if they still offer 
feedback, but I'm going to tighten things up again. **Please
adhere to the feedback thread rules.**

The place for freeform discussion is in the **Discussion Thread** 
at:

https://forum.dlang.org/post/wpqmuysuxadcwnzypnxk forum.dlang.org

You can find DIP 1044 here:

https://github.com/dlang/DIPs/blob/e2ca557ab9d3e60305a37da0d5b58299e0a9de0e/DIPs/DIP1044.md

The review period will end at 11:59 PM ET on December 3, or when 
I make a post declaring it complete. At that point, this thread 
will be considered closed and any subsequent feedback may be 
ignored at the DIP author's discretion.


Posts in this thread that do not adhere to the following rules 
will be deleted at the DIP author's discretion:

* All posts must be a direct reply to the DIP manager's initial 
post, with the following exceptions:
       - Any commenter may reply to their own posts to retract 
feedback contained in the original post;
       - The DIP author may (and is encouraged to) reply to any 
feedback solely to acknowledge the feedback with agreement or 
disagreement (preferably with supporting reasons in the latter 
case);
       - If the DIP author requests clarification on any specific 
feedback, the original commenter may reply with the extra 
information, and the DIP author may in turn reply as above.
* Feedback must be actionable, i.e., there must be some action 
the DIP author can choose to take in response to the feedback, 
such as changing details, adding new information, or even 
retracting the proposal.
* Feedback related to the merits of the proposal rather than to 
the contents of the DIP (e.g., "I'm against this DIP.") is 
allowed in Community Review (not Final Review), but must be 
backed by supporting arguments (e.g., "I'm against this DIP 
because..."). The supporting arguments must be reasonable. 
Obviously frivolous arguments waste everyone's time.
* Feedback should be clear and concise, preferably listed as 
bullet points (those who take the time to do an in-depth review 
and provide feedback in the form of answers to the questions in 
the documentation linked above will receive much gratitude). 
Information irrelevant to the DIP or which is not provided in 
service of clarifying the feedback is unwelcome.
Nov 18 2022
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 18 November 2022 at 15:38:59 UTC, Mike Parker wrote:


 This is the feedback thread for the first round of Community 
 Review of DIP 1044, "Enum Type Inference".
The second paragraph of the "Description" section reads as follows:
 In principle, ETI should be allowed anywhere that an enum type 
 is known unambiguously at compile-time. The following is a 
 (non-exhaustive) list of circumstances in which ETI will be 
 permitted.
This kind of vagueness is acceptable in a draft, but cannot be allowed in the final DIP. The description of the proposal must be complete and unambiguous. Remember: if this DIP is ever implemented, the implementer(s) will be relying on the DIP to determine what to implement, and the reviewers will also be relying on the DIP to determine whether the implementation is correct. Even if you plan to do the implementation yourself, the DIP must give the reviewers enough information that they can check your work.
Nov 18 2022
parent IchorDev <zxinsworld gmail.com> writes:
On Friday, 18 November 2022 at 16:38:47 UTC, Paul Backus wrote:
 This kind of vagueness is acceptable in a draft, but cannot be 
 allowed in the final DIP. The description of the proposal must 
 be complete and unambiguous.
I presume this means that the list of cases must therefore be exhaustive? I might need help from some experienced implementers to work through that, since there are probably many aspects of D's syntax that I am unfamiliar with. For instance, mixing enum types with `int`s in an array literal causes it to be `int[]`. Should ETI work in that case? The easy answer is "no". However, what if D already has a mechanism for figuring out what types have already been used in an array literal? If so, then ETI could work in that example without a special-case, unambiguously. I'd love if any implementers would like to talk about these sorts of cases on the discussion thread. If they are of the opinion that D's pre-existing implementation will not be hindered in the slightest by ETI having very precise implementation requirements, then I don't see why I can't write an exhaustive list of cases myself. Hopefully that clarifies things.
Nov 18 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
What happens in combination with function and template overloading is not 
discussed. For example:

     enum A { a }
     enum B { a }

     void ket(A);
     void ket(B);

     ...
     ket(a);
     ...

That's the simplest case. More complex cases come when there are multiple 
overloaded functions with diverse enum arguments, resulting in an unbounded 
combinatorial problem of which combination of enum members will be selected.

This kind of problem comes up whenever we contemplate adding top-down type 
inference and try to make it work in combination with the current bottom up 
method. It works in trivial cases as shown in the DIP, but the complex ones are 
the problem. (Even worse than the function overloading problem is the template 
overloading problem, as the compiler will need to instantiate the template with 
each combination of enum inferences just to figure out what the type of the 
template is.)
Nov 18 2022
parent reply IchorDev <zxinsworld gmail.com> writes:
On Friday, 18 November 2022 at 21:09:41 UTC, Walter Bright wrote:
 What happens in combination with function and template 
 overloading is not discussed.
Wrong: https://github.com/dlang/DIPs/blob/e2ca557ab9d3e60305a37da0d5b58299e0a9de0e/DIPs/DIP1044.md#other-considerations On Friday, 18 November 2022 at 21:09:41 UTC, Walter Bright wrote:
 That's the simplest case. More complex cases come when there 
 are multiple overloaded functions with diverse enum arguments, 
 resulting in an unbounded combinatorial problem of which 
 combination of enum members will be selected.
I must simply disagree. Unless there's some issue with an implementation along the lines of my previous post, I fail to see how this is more than a trivial syntax change that nobody is required to use if they don't want to. On Friday, 18 November 2022 at 21:09:41 UTC, Walter Bright wrote:
 [...] worse than the function overloading problem is the 
 template overloading problem, as the compiler will need to 
 instantiate the template with each combination of enum 
 inferences just to figure out what the type of the template is.)
Could you please provide a concrete example of this so I can better understand what you mean? (ideally one that I can run if I substitute out the ETI)
Nov 20 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/20/2022 8:52 AM, IchorDev wrote:
 Could you please provide a concrete example of this so I can better understand 
 what you mean? (ideally one that I can run if I substitute out the ETI)
enum A { a } enum B { a } void foo(A); void foo(B); auto bar(T)(T x) { return ....; } // is bar returning an A or a B? foo(bar!($a)); The template bar will have to be instantiated twice in order to determine the type of the argument being passed to foo(). If there are multiple arguments with $field somewhere in them, this is a combinatorial problem.
Nov 21 2022
parent reply IchorDev <zxinsworld gmail.com> writes:
On Monday, 21 November 2022 at 17:50:24 UTC, Walter Bright wrote:
 enum A { a }
 enum B { a }

 void foo(A);
 void foo(B);

 auto bar(T)(T x) { return ....; } // is bar returning an A or a 
 B?

 foo(bar!($a));
First thing: I'm going to assume you made a small mistake and actually meant `foo(bar($a));` instead of `foo(bar!($a));`. The latter would not compile anyway, since you would be trying to bind an enum member to a type. With that assumption in mind, your example would not compile once ETI is implemented, because according to the DIP:

 ETI is allowed in the argument lists of function calls and 
 template instantiations _when they can bind to **explicitly 
 typed**_ enum parameters.
```d enum A{ a,b,c,d } //[...] void myTempFn(T)(T param){} void main(){ //[...] myTempFn!A($a); myTempFn($a); // error, can't infer a type to instantiate the template with from "$a" } ``` Why is this? Well, `bar(T)(T x)` has no explicitly typed enum parameters! When you try to use ETI via `bar($a)` there will be no way for the compiler to infer the type of `a`, and you will get an ambiguity error. You might argue that your example shows you instantiating the `bar` template inside a call to `foo` which has 2 overloads that take an enum parameter. I want to clarify that—just like with nested arrays—this is completely meaningless to the evaluation of ETI. I will add examples covering this case in the next revision of the DIP so that there isn't any doubt it.
Nov 21 2022
parent Walter Bright <newshound2 digitalmars.com> writes:
On 11/21/2022 10:20 PM, IchorDev wrote:
 I will add examples covering this case in the next 
 revision of the DIP so that there isn't any doubt it.
Good, thank you.
Nov 21 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
The following is not addressed:

     enum E { e };
     int e;
     auto x = e;  // which e?

     void bahb(int);
     void bahb(E);
     bahb(e);  // which bahb?
Nov 18 2022
parent IchorDev <zxinsworld gmail.com> writes:
On Friday, 18 November 2022 at 21:29:15 UTC, Walter Bright wrote:
 The following is not addressed:

     enum E { e };
     int e;
     auto x = e;  // which e?

     void bahb(int);
     void bahb(E);
     bahb(e);  // which bahb?
Your example is written without using ETI. It should look like this if you want to use the enum member with ETI: ```d enum E { e } int e; auto x = $e; //E.e void bahb(int); void bahb(E x); bahb($e); //E.e ``` Otherwise the local variable will be used, because that's how D works.
Nov 21 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Scoping rules are not addressed. For example:

     enum E { e };

     void blue()
     {
         int e;
         auto x = e;  // which e?
     }
Nov 18 2022
parent IchorDev <zxinsworld gmail.com> writes:
On Friday, 18 November 2022 at 22:02:30 UTC, Walter Bright wrote:
 Scoping rules are not addressed. For example:

     enum E { e };

     void blue()
     {
         int e;
         auto x = e;  // which e?
     }
As with the previous message, this one does not use ETI at all. Here's an example with ETI: ```d E { e } blue(){ int e; auto x = $e; //E.e } ```
Nov 21 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Under "Prior Work", the C style of enums should be mentioned.

In C, enum members are *not* scoped, they are inserted into the same scope as 
the enum declaration:

     enum E { ex = 1; };

     void tab() {
         enum E x = ex; // ok
         enum E y = E.ex; // fail
     }

This behavior motivated a lot of C users (including me) to declare C enums
thusly:

     enum E { Eex = 1; };

     void tab() {
         enum E x = Eex;
     }

where the enum member name was prefixed with the enum name. This behavior was 
carried over into C++.

But C++ tired of it, and eventually introduced:

     enum class E { ex = 1; };

     void tab() {
         E x = ex; // fail
         E y = E::ex; // ok
     }

ImportC works by (internally) translating ImportC enums to D thusly:

     enum E { ex = 1; }
     alias ex = E.ex;    // <= re-declaring ex into enclosing scope

     void tab() {
         E x = ex; // ok
     }

All this leads to another way to get the result for D than using `with`:

     enum E { ex = 1; }
     alias ex = E.ex;    // <= re-declaring ex into enclosing scope

     void tab() {
         E x = ex; // ok
         E y = E.ex;  // also ok
     }

While this is a bit clumsy when there are lot of enum members, it suggests a 
special syntax for making it easy:

     enum NewKeywordHere E { ex = 1; }

     void tab() {
         E x = ex; // ok
         E y = E.ex;  // also ok
     }

I.e. going at this problem the opposite way C++ did.

I submit this is less disruptive to the language than adding $ and new lookup 
rules. It also puts the choice into the hands of the designer of the enum
rather 
than the user of the enum. Isn't it better to make this a choice the designer 
should have?

It inherently makes this an error:

     enum NewKeywordHere E { ex = 1; }
     int ex = 6; // error, ex is already defined

as it should be.
Nov 18 2022
parent IchorDev <zxinsworld gmail.com> writes:
On Saturday, 19 November 2022 at 02:35:19 UTC, Walter Bright 
wrote:
 Under "Prior Work", the C style of enums should be mentioned.
No. C-style enums are inserted into the same scope as the enum declaration, not an otherwise explicit declaration with a contextually inferred type.
 While this is a bit clumsy when there are lot of enum members, 
 it suggests a special syntax for making it easy:

     enum NewKeywordHere E { ex = 1; }

     void tab() {
         E x = ex; // ok
         E y = E.ex;  // also ok
     }

 I.e. going at this problem the opposite way C++ did.

 I submit this is less disruptive to the language than adding $ 
 and new lookup rules.
This proposal is inadequate as it may cause old code to break, which is actually disruptive to **users** of the language. It also makes it harder to distinguish between enums with inferred types and variables with the same names, and will cause confusing name conflicts.
  It also puts the choice into the hands of the designer of the 
 enum rather than the user of the enum. Isn't it better to make 
 this a choice the designer should have?
In practice, designers often make bad decisions. In my experience D has a highly flexible syntax that allows you to write ergonomic code in the face of awful library design.
Nov 21 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
With the syntax $e to look up an enum member, the compiler will need to search 
*every* enum that is in scope. Since one of D's strengths is whole program 
compilation, this can be slow. To speed that up, it will likely require that
the 
compiler maintain a hash table of all the enum fields.

I.e. a parallel symbol table will have to be maintained alongside the regular 
symbol table.
Nov 18 2022
parent IchorDev <zxinsworld gmail.com> writes:
On Saturday, 19 November 2022 at 02:54:01 UTC, Walter Bright 
wrote:
 With the syntax $e to look up an enum member, the compiler will 
 need to search *every* enum that is in scope. Since one of D's 
 strengths is whole program compilation, this can be slow. To 
 speed that up, it will likely require that the compiler 
 maintain a hash table of all the enum fields.

 I.e. a parallel symbol table will have to be maintained 
 alongside the regular symbol table.
This is a juncture where being an implementer gives you a lot more insight into this process than me. That said, please forgive (and correct) me if this sounds ridiculous and/or impossible… Here's a quick example: ```d enum A{ a,b,c } auto fn(int x){} auto fn(A x){} void main(){ fn($b); } ``` At `fn($b);` the compiler checks the signature of each overload of `fn` to see if any of them accept an enum type for this argument. The compiler ONLY finds `auto fn(A x)`, so it checks if `b` is a member of `A`. It is, so `$b` becomes `A.b`. Now, if we also define: ```d enum B{ a,b,c } auto fn(B x){} ``` The compiler finds `auto fn(A x)` AND `auto fn(B x)`, so it checks if `b` is a member of `A` or `B`... both checks return true, so the compiler returns an ambiguity error.
Nov 20 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
I'm looking at the DIP:

   auto y = [A.a, $b, $c, $d]; // typeof(y) = A[]
   auto z = [A.c, 64, $b, $b]; // typeof(z) = int[]

Note that the type of an array literal is determined by the common type of all 
the elements, not just the first element:

   auto a [ 0, 1, 3.0, 4 ]; // double[4]

The DIP says: "ETI is also allowed in array literals for which an explicit enum 
type can be inferred"

I'm not sure what the rule is. Is it the first element of enum type that sets 
the rule (left to right), or does:

   auto a = [ $a, A.b, $c ];

work?

What about nested arrays:

    auto a = [[A.b, $b], [$c, $d]];

?
Nov 18 2022
parent IchorDev <zxinsworld gmail.com> writes:
On Saturday, 19 November 2022 at 04:33:04 UTC, Walter Bright 
wrote:
 The DIP says: "ETI is also allowed in array literals for which 
 an explicit enum type can be inferred"

 I'm not sure what the rule is. Is it the first element of enum 
 type that sets the rule (left to right), or does:

   `auto a = [ $a, A.b, $c ];`

 work?
Before the structural pass the DIP explicitly stated that it was determined by the first element. I felt that this was enough to conclusively remove any ambiguity, however Micheal was of the opinion that this was not necessary, and removed it because:
 D already infers the type of literals when it can, so I don't 
 think there's any need to be verbose in describing it. The 
 examples speak well enough.
If what they said is not the case, do you feel that the explicit first-element rule should be re-added? On Saturday, 19 November 2022 at 04:33:04 UTC, Walter Bright wrote:
 What about nested arrays:

    `auto a = [[A.b, $b], [$c, $d]];`

 ?
Since `auto b = [[1,2],[0.3,0.4]];` gives a type incompatibility error, I think ETI should be consistent with this behaviour: The two arrays would be evaluated separately, so the second one would just look like `[$c, $d]` to the parser and it would return an error.
Nov 20 2022
prev sibling next sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
I know it sounds nitpicky, but I really dislike like acronym. 
“ETI” is used (at least) in C++ for “explicit template 
instantiation.” D has templates, so ETI can be a source for 
confusion. The term has been [used in the 
forum](https://forum.dlang.org/search?q=group%3AdigitalmarsD+%22explicit+templa
e+instantiation%22) unrelated to C++.

I don’t care for the wording of the DIP, but for what people will 
refer to it in the forum. If it makes it into the spec, it would 
be great if you could find something else. E.g. “type-inferred 
enums” (TIE) is not that different, IMO “type inferred enum 
members” (TIEM) is even better. Another one would be “implicit 
enum member” (IEM).
Nov 23 2022
parent IchorDev <zxinsworld gmail.com> writes:
On Wednesday, 23 November 2022 at 10:18:21 UTC, Quirin Schroll 
wrote:
 I know it sounds nitpicky, but I really dislike like acronym. 
 “ETI” is used (at least) in C++ for “explicit template 
 instantiation.” D has templates, so ETI can be a source for 
 confusion. The term has been [used in the 
 forum](https://forum.dlang.org/search?q=group%3AdigitalmarsD+%22explicit+templa
e+instantiation%22) unrelated to C++.

 I don’t care for the wording of the DIP, but for what people 
 will refer to it in the forum. If it makes it into the spec, it 
 would be great if you could find something else. E.g. 
 “type-inferred enums” (TIE) is not that different, IMO “type 
 inferred enum members” (TIEM) is even better. Another one would 
 be “implicit enum member” (IEM).
D is not C++. The DIP proposes enum type inference, which is a descriptive term. When the feature goes into the spec, I think it should be referred to as simply “type inference”, as it might not be specific to enums forever. I don't really know how to communicate that in the DIP, though. I feel that none of the terms you proposed really describe the feature accurately or succinctly in the same way as "enum type inference".
Nov 23 2022
prev sibling next sibling parent reply Daniel N <no public.email> writes:
On Friday, 18 November 2022 at 15:38:59 UTC, Mike Parker wrote:


 This is the feedback thread for the first round of Community 
 Review of DIP 1044, "Enum Type Inference".

 **THIS IS NOT A DISCUSSION THREAD**
I'm against this DIP because... 1) using new symbols $(or any other unused symbol) for a minor feature is wasteful, compared to adding new meta/codegen features etc. 2) Implicit 'with' for switch statements as proposed by Walter is more elegant and doesn't require any new symbols/syntax and is also more efficient from a compiler performance perspective as $ could refer to any enum, whereas the implicit 'with' only adds one enum. I propose changing the DIP to "implicit with".
Nov 23 2022
next sibling parent IchorDev <zxinsworld gmail.com> writes:
On Wednesday, 23 November 2022 at 16:11:32 UTC, Daniel N wrote:
 1) using new symbols $(or any other unused symbol) for a minor 
 feature is wasteful, compared to adding new meta/codegen 
 features etc.
It is a minor feature at the moment. There's nothing stopping it exclusively for `#line`, which prohibits its use almost anywhere else (except strings), but I am not against that either. `$` can still be used as a binary operator in the future, or in regular strings for interpolation or what-have-you.
 2) Implicit 'with' for switch statements as proposed by Walter 
 is more elegant and doesn't require any new symbols/syntax and 
 is also more efficient from a compiler performance perspective 
 as $ could refer to any enum, whereas the implicit 'with' only 
 adds one enum.
If it only works in `case` statements then it does not functionally replace the usefulness of ETI: ```d enum MyEnum{ a,b,c,d } struct Obj{ MyEnum one, two; } void myFn(MyEnum param){} void myDefaultFn(MyEnum param=$d){} void myTempFn(alias x: MyEnum)(){} void main(){ Obj myS1 = {one: $a, two: $b}; auto myS2 = Obj($a, $b); myFn($a); myFn($b + $b); myDefaultFn(); myDefaultFn($c); myTempFn($a); } ``` You might argue that an explicit `with(T)` would be nice in the above example. Well, it's an example. In my experience, enum usage tends to be very spread out across codebases. Therefore using `with(T)` would lead to me doing *more* typing because I would need 1 `with` per enum use.
Nov 24 2022
prev sibling parent IchorDev <zxinsworld gmail.com> writes:
On Wednesday, 23 November 2022 at 16:11:32 UTC, Daniel N wrote:
 [implicit 'with'] is more efficient from a compiler performance 
 perspective as $ could refer to any enum, whereas the implicit 
 'with' only adds one enum.
I thought I should also add that this is completely false. In a `case` statement, `$member` can only refer to a member of the type enum type used in the `switch`. If it does not, the compiler will return an error.
Nov 24 2022
prev sibling parent reply XavierAP <n3minis-git yahoo.es> writes:
On Friday, 18 November 2022 at 15:38:59 UTC, Mike Parker wrote:
 You can find DIP 1044 here:

 https://github.com/dlang/DIPs/blob/e2ca557ab9d3e60305a37da0d5b58299e0a9de0e/DIPs/DIP1044.md
The "rationale" is virtually empty, it contains only a personal statement that qualifying enums "can be tedious". Could this (essential) first part of the DIP be extended, explaining what the problem is that it aims to solve, providing some use cases -- and why is the proposed change a better solution than any possible with the current language, and the best possible solution in general?
Nov 27 2022
parent IchorDev <zxinsworld gmail.com> writes:
On Sunday, 27 November 2022 at 09:00:50 UTC, XavierAP wrote:
 The "rationale" is virtually empty, it contains only a personal 
 statement that qualifying enums "can be tedious". Could this 
 (essential) first part of the DIP be extended, explaining what 
 the problem is that it aims to solve, providing some use cases 
 -- and why is the proposed change a better solution than any 
 possible with the current language, and the best possible 
 solution in general?
Good point, it is relatively short. It's hard to write a long and detailed rationale for a feature as small as a simple shortcut to alleviate unnecessarily repeating yourself in your code. That said, I will try and expand the rationale section but it might not be pretty. I'm a programmer not an author, after all.
Nov 30 2022