digitalmars.D - Feedback Thread: DIP 1044--Enum Type Inference--Community Review Round
- Mike Parker (50/50) Nov 18 2022 ## Feedback Thread
- Paul Backus (12/19) Nov 18 2022 The second paragraph of the "Description" section reads as
- IchorDev (17/20) Nov 18 2022 I presume this means that the list of cases must therefore be
- Walter Bright (19/19) Nov 18 2022 What happens in combination with function and template overloading is no...
- IchorDev (12/22) Nov 20 2022 Wrong:
- Walter Bright (10/12) Nov 21 2022 enum A { a }
- IchorDev (28/39) Nov 21 2022 First thing: I'm going to assume you made a small mistake and
- Walter Bright (2/4) Nov 21 2022 Good, thank you.
- Walter Bright (7/7) Nov 18 2022 The following is not addressed:
- IchorDev (14/21) Nov 21 2022 Your example is written without using ETI.
- Walter Bright (7/7) Nov 18 2022 Scoping rules are not addressed. For example:
- IchorDev (10/17) Nov 21 2022 As with the previous message, this one does not use ETI at all.
- Walter Bright (50/50) Nov 18 2022 Under "Prior Work", the C style of enums should be mentioned.
- IchorDev (13/27) Nov 21 2022 No. C-style enums are inserted into the same scope as the enum
- Walter Bright (6/6) Nov 18 2022 With the syntax $e to look up an enum member, the compiler will need to ...
- IchorDev (27/34) Nov 20 2022 This is a juncture where being an implementer gives you a lot
- Walter Bright (15/15) Nov 18 2022 I'm looking at the DIP:
- IchorDev (15/27) Nov 20 2022 Before the structural pass the DIP explicitly stated that it was
- Quirin Schroll (11/11) Nov 23 2022 I know it sounds nitpicky, but I really dislike like acronym.
- IchorDev (10/21) Nov 23 2022 D is not C++. The DIP proposes enum type inference, which is a
- Daniel N (11/15) Nov 23 2022 I'm against this DIP because...
- IchorDev (30/38) Nov 24 2022 It is a minor feature at the moment. There's nothing stopping it
- IchorDev (5/8) Nov 24 2022 I thought I should also add that this is completely false. In a
- XavierAP (8/10) Nov 27 2022 The "rationale" is virtually empty, it contains only a personal
- IchorDev (6/13) Nov 30 2022 Good point, it is relatively short. It's hard to write a long and
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
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
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
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
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
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
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
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
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
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
Scoping rules are not addressed. For example: enum E { e }; void blue() { int e; auto x = e; // which e? }
Nov 18 2022
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
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
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
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
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
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
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
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
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
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
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
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
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.mdThe "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
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