www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Enum literals, good? bad? what do you think?

reply russhy <russhy gmail.com> writes:
Hello

I all the time wondered why we always have to be so much verbose 
with enum, when it's not casting to primitives, it is about 
repeating their long type name, constantly, all the time

After trying some other languages over the past few years, i 
discovered in zig you can just ommit the enum type name and just 
use enums this way: .MY_VALUE

I don't know if that's something that could be supported with D, 
i am not a compiler dude, so i don't have the answer

Adam on discord mentioned using with(ENUM_TYPE) or just an alias, 
but i think we go ahead and make it simple

I had prepared a DIP [1], not ready at all, but i wanted to 
initiate some discussion about that feature

So what do you think? yay? nay? why not?


[1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
Jul 20 2021
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 20, 2021 at 03:50:49PM +0000, russhy via Digitalmars-d wrote:
 Hello
 
 I all the time wondered why we always have to be so much verbose with
 enum, when it's not casting to primitives, it is about repeating their
 long type name, constantly, all the time
[...] OT1H, having to qualify enums by their full name is good, it helps to avoid the mess in C where libraries sometimes define conflicting values under the same name, e.g., // somelib.h #define ERROR 0 #define OK 1 // someotherlib.h #define ERROR -1 #define OK 0 In D, if you used an enum, you'd have to quality which ERROR or OK you're referring to, which avoids conflicts and also avoids unexpected symbol hijacking. E.g., somelib.h used to define MY_OK = 1, but after upgrading they renamed MY_OK to OK, now your code that referred to OK in someotherlib.h may accidentally get the wrong value. OTOH, I agree that sometimes D enums become rather verbose. Especially in switch statements where you have to repeat their name for every case. Fortunately, this is where D's `with` statement comes in helpful: enum MyEnum { blah, bleh, bluh } MyEnum val = ...; final switch (val) { case MyEnum.blah: ... case MyEnum.bleh: ... case MyEnum.bluh: ... } can be replaced with: enum MyEnum { blah, bleh, bluh } MyEnum val = ...; final switch (val) with (MyEnum) { case blah: ... // look ma! less repetition! case bleh: ... case bluh: ... } T -- Questions are the beginning of intelligence, but the fear of God is the beginning of wisdom.
Jul 20 2021
next sibling parent russhy <russhy gmail.com> writes:
On Tuesday, 20 July 2021 at 16:12:05 UTC, H. S. Teoh wrote:
 On Tue, Jul 20, 2021 at 03:50:49PM +0000, russhy via 
 Digitalmars-d wrote:
 Hello
 
 I all the time wondered why we always have to be so much 
 verbose with enum, when it's not casting to primitives, it is 
 about repeating their long type name, constantly, all the time
[...] OT1H, having to qualify enums by their full name is good, it helps to avoid the mess in C where libraries sometimes define conflicting values under the same name, e.g., // somelib.h #define ERROR 0 #define OK 1 // someotherlib.h #define ERROR -1 #define OK 0 In D, if you used an enum, you'd have to quality which ERROR or OK you're referring to, which avoids conflicts and also avoids unexpected symbol hijacking. E.g., somelib.h used to define MY_OK = 1, but after upgrading they renamed MY_OK to OK, now your code that referred to OK in someotherlib.h may accidentally get the wrong value. OTOH, I agree that sometimes D enums become rather verbose. Especially in switch statements where you have to repeat their name for every case. Fortunately, this is where D's `with` statement comes in helpful: enum MyEnum { blah, bleh, bluh } MyEnum val = ...; final switch (val) { case MyEnum.blah: ... case MyEnum.bleh: ... case MyEnum.bluh: ... } can be replaced with: enum MyEnum { blah, bleh, bluh } MyEnum val = ...; final switch (val) with (MyEnum) { case blah: ... // look ma! less repetition! case bleh: ... case bluh: ... } T
That's a good point, and that ``switch with`` is very nice and solve the issue I didn't know about ``with`` until adam mentioned it, could the rule in ``switch`` be relaxed and implement something akin to ``with`` ?
Jul 20 2021
prev sibling next sibling parent Max Samukha <maxsamukha gmail.com> writes:
On Tuesday, 20 July 2021 at 16:12:05 UTC, H. S. Teoh wrote:

 	enum MyEnum { blah, bleh, bluh }
 	MyEnum val = ...;
 	final switch (val) with (MyEnum) {
 		case blah: ...	// look ma! less repetition!
 		case bleh: ...
 		case bluh: ...
 	}
Cool hack!
Jul 20 2021
prev sibling next sibling parent reply claptrap <clap trap.com> writes:
On Tuesday, 20 July 2021 at 16:12:05 UTC, H. S. Teoh wrote:
 On Tue, Jul 20, 2021 at 03:50:49PM +0000, russhy via 
 Digitalmars-d wrote:
 Hello
 
 I all the time wondered why we always have to be so much 
 verbose with enum, when it's not casting to primitives, it is 
 about repeating their long type name, constantly, all the time
[...] OT1H, having to qualify enums by their full name is good, it helps to avoid the mess in C where libraries sometimes define conflicting values under the same name, e.g., // somelib.h #define ERROR 0 #define OK 1 // someotherlib.h #define ERROR -1 #define OK 0 In D, if you used an enum, you'd have to quality which ERROR or OK you're referring to, which avoids conflicts and also avoids
That's irrelevant i think, since #defines are like anonymous enums, and you dont need to save typing on those. With named enums in D i think you'd know (most or all? of the time) which one it is by the context... Eg.. enum SomeResult { OK, Error } enum OtherResult { Error, OK } You cant assign or pass SomeResult.OK to something that is expecting an OtherResult can you? So if you have a function... void handleError(OtherResult r); and call it thus... handleError(OK); You know it's OtherResult.OK Isnt that the point of strong typing?
Jul 20 2021
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 7/20/21 1:43 PM, claptrap wrote:

 So if you have a function...

 void handleError(OtherResult r);

 and call it thus...

 handleError(OK);

 You know it's OtherResult.OK

 Isnt that the point of strong typing?
Most of the time yes, but our enums are not that strong. :) The following compiles and runs with both types. enum Animal { Jaguar = 100 } enum Car { Jaguar = 42 } void foo(int i) { } void main() { foo(Animal.Jaguar); foo(Car.Jaguar); } But the your argument will probably be foo(Jaguar); should work as long as there is no conflict. But then there is name hijacking. What if that last expression worked and let's say only Car.Jaguar was in scope. What if I imported an unrelated module that defined this: int Jaguar = 0; I would expect foo(Jaguar) to still work with that int without complaints because I did not specify the enum version. But then my program works differently. (In other words, I would not expect the compiler to complain about a name conflict between an module-level name and an enum-protected name; that's the whole point anyway.) In any case, I don't have any problem with extra typing and happy with D's safer enums. I experimented with the with(Enum) helper but don't use it anymore. Not a big deal. :/ Ali
Jul 20 2021
parent reply claptrap <clap trap.com> writes:
On Tuesday, 20 July 2021 at 21:15:19 UTC, Ali Çehreli wrote:
 On 7/20/21 1:43 PM, claptrap wrote:

 So if you have a function...

 void handleError(OtherResult r);

 and call it thus...

 handleError(OK);

 You know it's OtherResult.OK

 Isnt that the point of strong typing?
Most of the time yes, but our enums are not that strong. :) The following compiles and runs with both types. enum Animal { Jaguar = 100 } enum Car { Jaguar = 42 } void foo(int i) { } void main() { foo(Animal.Jaguar); foo(Car.Jaguar); } But the your argument will probably be foo(Jaguar);
That that would be an error since the parameter is an 'int' so you dont have any context to decide what enum it should refer to. IE.. void foo(int i); void bar(Animal a); foo(Animal.Jaguar); // OK foo(Jaguar); // Error bar(Jaguar); // OK likewise auto a = Jaguar; // Error Car c = Jaguar; // OK The point is you should only be able to drop the enum name if the parameter or the variable you're assigning to are ***named enums***. I suppose for switch you could do the same, if the variable being switched on is a named enum, then the cases can drop the name.
Jul 20 2021
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 7/20/21 2:52 PM, claptrap wrote:

 The point is you should only be able to drop the enum name if the
 parameter or the variable you're assigning to are ***named enums***.
Agreed. There wouldn't be name hijacking in that case. Either works helpfully or errors with a conflict.
 I
 suppose for switch you could do the same, if the variable being switched
 on is a named enum, then the cases can drop the name.
Agreed with that as well. The type of the switch variable could hint the compiler. Ali
Jul 20 2021
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/20/2021 9:12 AM, H. S. Teoh wrote:
 OTOH, I agree that sometimes D enums become rather verbose. Especially
 in switch statements where you have to repeat their name for every case.
 Fortunately, this is where D's `with` statement comes in helpful:
 
 	enum MyEnum { blah, bleh, bluh }
 	MyEnum val = ...;
 	final switch (val) {
 		case MyEnum.blah: ...
 		case MyEnum.bleh: ...
 		case MyEnum.bluh: ...
 	}
In C, qualifying the member name with the tag name is not only not necessary, it is not allowed. Hence, here how C does it: enum MyEnum { MyEnum_blah, MyEnum_bleh, MyEnum_bluh }; enum MyEnum val = ...; switch (val) { case MyEnum_blah: ... case MyEnum_bleh: ... case MyEnum_bluh: ... } It's not very attractive. C++ eventually added some more syntax so the members had to be qualified. This is because once the project exceeds a certain level of complexity, qualifying those enum member names becomes desirable. But if you still want unqualified names, `alias` is the feature: enum MyEnum { blah, bleh, bluh } alias blah = MyEnum.blah; alias bleh = MyEnum.bleh; alias bluh = MyEnum.bluh; `alias` is an all-purpose tool for moving names from one scope to another. Of course, one can probably do a mixin to automate the alias declarations. It should be a fun exercise. Any takers? (As mentioned by others, `with` does it too, but `with` only affects the scope it specifies.)
Jul 20 2021
next sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Wednesday, 21 July 2021 at 06:39:18 UTC, Walter Bright wrote:
[...]
 But if you still want unqualified names, `alias` is the feature:


     enum MyEnum { blah, bleh, bluh }
     alias blah = MyEnum.blah;
     alias bleh = MyEnum.bleh;
     alias bluh = MyEnum.bluh;
 `alias` is an all-purpose tool for moving names from one scope 
 to another. Of course, one can probably do a mixin to automate 
 the alias declarations. It should be a fun exercise. Any takers?
mixin({ auto aliasList = ""; static foreach(m; __traits(allMembers, MyEnum)) aliasList ~= "alias "~m~" = MyEnum."~m~";\n" return aliasList; }());
 (As mentioned by others, `with` does it too, but `with` only 
 affects the scope it specifies.)
Jul 21 2021
parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 12:11 AM, Patrick Schluter wrote:
 `alias` is an all-purpose tool for moving names from one scope to another. Of 
 course, one can probably do a mixin to automate the alias declarations. It 
 should be a fun exercise. Any takers?
mixin({   auto aliasList = "";   static foreach(m; __traits(allMembers, MyEnum))     aliasList ~= "alias "~m~" = MyEnum."~m~";\n"   return aliasList; }());
Sweet! Please put in the D wiki somewhere!
Jul 21 2021
prev sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Wednesday, 21 July 2021 at 06:39:18 UTC, Walter Bright wrote:

 But if you still want unqualified names, `alias` is the feature:


     enum MyEnum { blah, bleh, bluh }
     alias blah = MyEnum.blah;
     alias bleh = MyEnum.bleh;
     alias bluh = MyEnum.bluh;

 `alias` is an all-purpose tool for moving names from one scope 
 to another. Of course, one can probably do a mixin to automate 
 the alias declarations. It should be a fun exercise. Any takers?

 (As mentioned by others, `with` does it too, but `with` only 
 affects the scope it specifies.)
```d enum expandEnum(EnumType, string fqnEnumType = EnumType.stringof) = (){ string expandEnum = "enum {"; foreach(m;__traits(allMembers, EnumType)) { expandEnum ~= m ~ " = " ~ fqnEnumType ~ "." ~ m ~ ","; } expandEnum ~= "}"; return expandEnum; }(); enum MyEnum { blah, bleh, bluh } mixin(expandEnum!MyEnum); auto e = blah; ```
Jul 21 2021
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 12:12 AM, Mike Parker wrote:
 enum expandEnum(EnumType, string fqnEnumType = EnumType.stringof) = (){
      string expandEnum = "enum {";
      foreach(m;__traits(allMembers, EnumType)) {
          expandEnum ~= m ~ " = " ~ fqnEnumType ~ "." ~ m ~ ",";
      }
      expandEnum  ~= "}";
      return expandEnum;
 }();
 
 enum MyEnum { blah, bleh, bluh }
 mixin(expandEnum!MyEnum);
 
 auto e = blah;
You & Patrick are da men! This would make a nice bite-sized article showing off D's metaprogramming facilities.
Jul 21 2021
prev sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Wednesday, 21 July 2021 at 07:12:25 UTC, Mike Parker wrote:
 ```d
 enum expandEnum(EnumType, string fqnEnumType = 
 EnumType.stringof) = (){
Eeek, this is awful. It uses .stringof which means it is automatically stinky. It is liable for trouble when anything other than basic imports get involved. If you are going to do this kind of thing as a mixin at all, a mixin template is far more appropriate. It avoids the scope trouble since it can use the unambiguous local name. But when there's `with`.... none of this stuff gains you anything. It doesn't get closer to the Swift examples.
Jul 21 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 3:22 PM, Adam D Ruppe wrote:
 On Wednesday, 21 July 2021 at 07:12:25 UTC, Mike Parker wrote:
 ```d
 enum expandEnum(EnumType, string fqnEnumType = EnumType.stringof) = (){
Eeek, this is awful. It uses .stringof which means it is automatically stinky. It is liable for trouble when anything other than basic imports get involved.
Just use it right after the declaration of the enum.
 It doesn't 
 get closer to the Swift examples.
How so?
Jul 21 2021
parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Wednesday, 21 July 2021 at 23:00:06 UTC, Walter Bright wrote:
 Just use it right after the declaration of the enum.
That limits the usefulness, but you still might as well do it right since then it works anywhere.
 It doesn't get closer to the Swift examples.
How so?
Consider: import a; import b; foo(something); bar(something); foo and bar take different types that both have a member something. the swift examples resolve correctly. This does not and requires disambiguation on both uses.
Jul 21 2021
parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 4:32 PM, Adam D Ruppe wrote:
 Consider:
 
 import a;
 import b;
 
 foo(something);
 bar(something);
 
 foo and bar take different types that both have a member something. the swift 
 examples resolve correctly. This does not and requires disambiguation on both
uses.
True, but that isn't specific to enums. D does not attempt to do overloading on anything but functions & templates. Implementing russhy's proposal won't change that.
Jul 21 2021
prev sibling next sibling parent Mike Parker <aldacron gmail.com> writes:
On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:

 So what do you think? yay? nay? why not?
Personally, I have no issue with the current setup. I like knowing exactly where the enum values are coming from, and the existing tools to cut down on the typing (like with) are sufficient. But if you do want to propose a solution, I don't think `.foo` is the way to go. It looks too much like the module scope operator: https://dlang.org/spec/module.html#module_scope_operators I have no idea what that would mean for the implementation (and you'd need to account for that in the DIP), but visually it would be an unnecessary speed bump when mentally parsing the code.
Jul 20 2021
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 So what do you think? yay? nay? why not?


 [1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
Currently, name lookup in D is done independently from type-checking. This proposal would require name lookup to depend on type checking in order to compile code like the following example: ```d enum A { x } enum B { x } A var = .x; ``` I don't think adding this kind of complexity to the language semantics is worth it just to save a few characters.
Jul 20 2021
parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Tuesday, 20 July 2021 at 16:34:13 UTC, Paul Backus wrote:
 On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 So what do you think? yay? nay? why not?


 [1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
Currently, name lookup in D is done independently from type-checking. This proposal would require name lookup to depend on type checking in order to compile code like the following example: ```d enum A { x } enum B { x } A var = .x; ```
One could argue that function overloading is a case where name resolution already depends on type checking. In a sense, `x` (in the above snippet) would be an overloaded name, which can resolved in a given context. Note that the question of overloading manifest constants was raised when the first-class function [DIP][1] was active. If that DIP is revisited, the question would need to be resolved.
 I don't think adding this kind of complexity to the language 
 semantics is worth it just to save a few characters.
I don't mind keeping D simpler, but I want to point out that the complexity depends on the scope of the feature. Function calls are likely harder to implement due to overloading, based on the comment [here][2], but target-typed variable initialization and assignment is likely more straightforward. OTOH, allowing target-typed literals in one place (variable init / assignment) and not in other (function calls) could be regarded as a pointless and artificial design limitation (similar to how struct literals are allowed only for [variable initialization][3]). * https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#fit-and-finish-features * https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new [1]: https://github.com/dlang/DIPs/pull/16#discussion-diff-71641427 [2]: https://issues.dlang.org/show_bug.cgi?id=16437#c2 [3]: https://dlang.org/spec/struct#static_struct_init
Jul 21 2021
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 8:13 AM, Petar Kirov [ZombineDev] wrote:
 similar to how struct literals are allowed only 
 for [variable initialization][3]
Struct literals can be anywhere once named parameters are implemented. They already can be if you don't need to use named parameters.
Jul 21 2021
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 21 July 2021 at 15:13:36 UTC, Petar Kirov 
[ZombineDev] wrote:
 OTOH, allowing target-typed literals in one place (variable 
 init / assignment) and not in other (function calls) could be 
 regarded as a pointless and artificial design limitation 
 (similar to how struct literals are allowed only for [variable 
 initialization][3]).



 * 
 https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#fit-and-finish-features
 * 
 https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new

 [3]: https://dlang.org/spec/struct#static_struct_init
IMO D's target-typed literals (also called "polysemous literals") are also a misfeature. They lead to weird, unintuitive situations where seemingly-equivalent code has different behavior; for example: ```d import std.stdio; void fun(double[] arr) { writeln("double"); } void fun(T)(T[] arr) { writeln(T.stringof); } void main() { fun([1, 2, 3]); auto arr = [1, 2, 3]; fun(arr); } ``` A language with fully-context-aware type inference would infer both arrays as `double[]`. A language with fully-context-independent type inference would infer both as `int[]`. In D, however, we get the worst of both worlds: type inference is *mostly* context-independent (so we have to add type annotations even in cases that are unambiguous), but there are a few special cases where context is taken into account (so we have to be mindful of context when refactoring, since a change in context could change the inferred type of an expression).
Jul 22 2021
parent John Colvin <john.loughran.colvin gmail.com> writes:
On Thursday, 22 July 2021 at 16:41:30 UTC, Paul Backus wrote:
 IMO D's target-typed literals (also called "polysemous 
 literals") are also a misfeature. They lead to weird, 
 unintuitive situations where seemingly-equivalent code has 
 different behavior; for example:

 ```d
 import std.stdio;

 void fun(double[] arr) { writeln("double"); }
 void fun(T)(T[] arr) { writeln(T.stringof); }

 void main()
 {
     fun([1, 2, 3]);
     auto arr = [1, 2, 3];
     fun(arr);
 }
 ```

 A language with fully-context-aware type inference would infer 
 both arrays as `double[]`. A language with 
 fully-context-independent type inference would infer both as 
 `int[]`.

 In D, however, we get the worst of both worlds: type inference 
 is *mostly* context-independent (so we have to add type 
 annotations even in cases that are unambiguous), but there are 
 a few special cases where context is taken into account (so we 
 have to be mindful of context when refactoring, since a change 
 in context could change the inferred type of an expression).
It’s not so bad IMO. Type inference in D isn’t far from just “forward, one step at a time”, which is easy to understand but still very powerful.
Jul 22 2021
prev sibling next sibling parent Basile B. <b2.temp gmx.com> writes:
On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 Hello

 I all the time wondered why we always have to be so much 
 verbose with enum, when it's not casting to primitives, it is 
 about repeating their long type name, constantly, all the time

 After trying some other languages over the past few years, i 
 discovered in zig you can just ommit the enum type name and 
 just use enums this way: .MY_VALUE

 I don't know if that's something that could be supported with 
 D, i am not a compiler dude, so i don't have the answer

 Adam on discord mentioned using with(ENUM_TYPE) or just an 
 alias, but i think we go ahead and make it simple

 I had prepared a DIP [1], not ready at all, but i wanted to 
 initiate some discussion about that feature

 So what do you think? yay? nay? why not?


 [1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
In the past [I liked the idea](https://issues.dlang.org/show_bug.cgi?id=16437), so it's a _why not_ for me. From what I remember of the experiment this was not pleasant to implement. It required an array of enum declaration to be added to the internal `Scope` class.
Jul 20 2021
prev sibling next sibling parent Danni Coy <danni.coy gmail.com> writes:
On Wed, Jul 21, 2021 at 1:56 AM russhy via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 Hello

 I all the time wondered why we always have to be so much verbose
 with enum, when it's not casting to primitives, it is about
 repeating their long type name, constantly, all the time

 After trying some other languages over the past few years, i
 discovered in zig you can just ommit the enum type name and just
 use enums this way: .MY_VALUE

 I don't know if that's something that could be supported with D,
 i am not a compiler dude, so i don't have the answer

 Adam on discord mentioned using with(ENUM_TYPE) or just an alias,
 but i think we go ahead and make it simple

 I had prepared a DIP [1], not ready at all, but i wanted to
 initiate some discussion about that feature

 So what do you think? yay? nay? why not?


 [1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
When I work in C/C++ and other similar languages, I find that on anything beyound a trivial program and particularly in a library a namespace gets baked into the variable name. When using D I find that the verbosity works out to be the same for example SDL_INIT_VIDEO becomes SDL_INIT.VIDEO and then I end up with less verbosity in a switch using with as other people have mentioned.
Jul 20 2021
prev sibling next sibling parent reply Mathias LANG <geod24 gmail.com> writes:
On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 So what do you think? yay? nay? why not?


 [1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
The `.` syntax is ambiguous because `.` is the module-scope operator. There's also the question of what to do with nested enums: ``` struct Foo { enum Bar { A, B, } void func () { assert(A == 0); // Should it work ? assert(.A == 0); // Or should it be this ? // The above currently refers to the `struct A`. } } struct A {} ``` The only time I had found enums to be too verbose is in switch statements. As mentioned previously, `switch (value) with `(EnumType)` helps. I think we should make it the default (essentially `case`s should be in a `with (typeof(value))` scope), and that would alleviate most of the pain.
Jul 20 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/20/2021 8:10 PM, Mathias LANG wrote:
 struct Foo
 {
      enum Bar { A, B, }
 
      void func () {
          assert(A == 0); // Should it work ?
If you want anonymous enums, use: enum { A, B }
          assert(.A == 0); // Or should it be this ?
Then you'd have no way to access a module scope symbol with the same name.
          // The above currently refers to the `struct A`.
      }
 }
 
 struct A {}
 ```
 
 The only time I had found enums to be too verbose is in switch statements.
 As mentioned previously, `switch (value) with `(EnumType)` helps.
Yup.
 I think we should make it the default (essentially `case`s should be in a
`with 
 (typeof(value))` scope), and that would alleviate most of the pain.
Sorry, I'm not understanding the pain.
Jul 20 2021
parent reply Mathias LANG <geod24 gmail.com> writes:
On Wednesday, 21 July 2021 at 06:19:20 UTC, Walter Bright wrote:
 
 The only time I had found enums to be too verbose is in switch 
 statements.
 As mentioned previously, `switch (value) with `(EnumType)` 
 helps.
Yup.
 I think we should make it the default (essentially `case`s 
 should be in a `with (typeof(value))` scope), and that would 
 alleviate most of the pain.
Sorry, I'm not understanding the pain.
What I meant was that instead of recommending our users to use `switch (value) with `(EnumType)`, we could just make it the default since it's the common case with enums. Unless we think that requiring the type of the enum be spelt out is better. It could add to the long list of those usability enhancements that are a hallmark of D (like `_` in number literals). I personally don't mind the verbosity, but I heard this complaint a few times over the years.
Jul 21 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 8:47 PM, Mathias LANG wrote:
 What I meant was that instead of recommending our users to use `switch (value) 
 with `(EnumType)`, we could just make it the default since it's the common
case 
 with enums. Unless we think that requiring the type of the enum be spelt out
is 
 better.
 
 It could add to the long list of those usability enhancements that are a 
 hallmark of D (like `_` in number literals). I personally don't mind the 
 verbosity, but I heard this complaint a few times over the years.
Ok, I see what you mean. If it was restricted to the case value expressions, there is merit to the idea. But suppose: enum A = 3, B = 4; switch (x) { case A: ... case B: ... } there isn't much of a clue that `case A` is not `case 3`, which matters more and more the larger the program gets. One thing I've learned with D is that programmers often have mental models of how symbol lookup is done that diverge substantially from how it actually works, and I've never been able to communicate how things actually work to them. Putting a special case in like this is pretty risky.
Jul 21 2021
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
I can see the Scott Meyers slide on "Quirks Of D" already:

"You would think that this would be `case 3`. But noooo, you'd be wrong!"
Jul 21 2021
prev sibling next sibling parent Mathias LANG <geod24 gmail.com> writes:
On Thursday, 22 July 2021 at 04:37:43 UTC, Walter Bright wrote:
 there isn't much of a clue that `case A` is not `case 3`, which 
 matters more and more the larger the program gets.
That's a very good point, I didn't think about local enum when trying to come up with corner cases. And IMO that's a dealbreaker for this idea, since local enums for case statements aren't that rare, at least in the code I've worked with.
Jul 21 2021
prev sibling next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 22/07/2021 4:37 PM, Walter Bright wrote:
 One thing I've learned with D is that programmers often have mental 
 models of how symbol lookup is done that diverge substantially from how 
 it actually works, and I've never been able to communicate how things 
 actually work to them.
If you can't describe it using words, use code instead. That logic in the compiler could be a great example of your desire to distill it down. If the logic and data is encapsulated well, it could look very pseudo-code-ish!
Jul 21 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 11:28 PM, rikki cattermole wrote:
 If you can't describe it using words, use code instead.
I tried everything, including that. I really cannot explain it. It was a very frustrating experience.
Jul 22 2021
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 22/07/2021 8:37 PM, Walter Bright wrote:
 On 7/21/2021 11:28 PM, rikki cattermole wrote:
 If you can't describe it using words, use code instead.
I tried everything, including that. I really cannot explain it. It was a very frustrating experience.
Oh hmm. I just went looking for it, the only files regarding semantic analysis I thought it could be in yup... 700 LOC method. Found that rather quickly. That really isn't a good sign I must admit!
Jul 22 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/22/2021 3:03 AM, rikki cattermole wrote:
 
 On 22/07/2021 8:37 PM, Walter Bright wrote:
 On 7/21/2021 11:28 PM, rikki cattermole wrote:
 If you can't describe it using words, use code instead.
I tried everything, including that. I really cannot explain it. It was a very frustrating experience.
Oh hmm. I just went looking for it, the only files regarding semantic analysis I thought it could be in yup... 700 LOC method. Found that rather quickly. That really isn't a good sign I must admit!
The symbol lookup used to be very simple and completely consistent in D. It got a *lot* more complicated as a result of my inability to explain how it worked. It does two passes now, and is very inconsistent.
Jul 22 2021
parent Tejas <notrealemail gmail.com> writes:
On Thursday, 22 July 2021 at 17:28:02 UTC, Walter Bright wrote:
 On 7/22/2021 3:03 AM, rikki cattermole wrote:
 
 On 22/07/2021 8:37 PM, Walter Bright wrote:
 On 7/21/2021 11:28 PM, rikki cattermole wrote:
 If you can't describe it using words, use code instead.
I tried everything, including that. I really cannot explain it. It was a very frustrating experience.
Oh hmm. I just went looking for it, the only files regarding semantic analysis I thought it could be in yup... 700 LOC method. Found that rather quickly. That really isn't a good sign I must admit!
The symbol lookup used to be very simple and completely consistent in D. It got a *lot* more complicated as a result of my inability to explain how it worked. It does two passes now, and is very inconsistent.
You mean this isn't true anymore? https://dlang.org/spec/module.html#name_lookup
Jul 22 2021
prev sibling next sibling parent zjh <fqbqrr 163.com> writes:
On Thursday, 22 July 2021 at 04:37:43 UTC, Walter Bright wrote:
 On 7/21/2021 8:47 PM, Mathias LANG wrote:
 What I meant was that instead of recommending our users to use
and I've never been
 able to communicate how things actually work to them.
Because the forum can not label the post, and We didn't sort out the `answers and posts`. So, you answer it `over and over again`. And You will be asked the same question again and again. We need to mobilize the community to submit what they think `excellent and useful` answers and label them for classify. And specially put in a section of the forum like `Good Answers and Posts`, And should add `label` function to the `posts` and `answers` for the forum.
Jul 21 2021
prev sibling next sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 22 July 2021 at 04:37:43 UTC, Walter Bright wrote:
 On 7/21/2021 8:47 PM, Mathias LANG wrote:
 What I meant was that instead of recommending our users to use 
 `switch (value) with `(EnumType)`, we could just make it the 
 default since it's the common case with enums. Unless we think 
 that requiring the type of the enum be spelt out is better.
 
 It could add to the long list of those usability enhancements 
 that are a hallmark of D (like `_` in number literals). I 
 personally don't mind the verbosity, but I heard this 
 complaint a few times over the years.
Ok, I see what you mean. If it was restricted to the case value expressions, there is merit to the idea. But suppose: enum A = 3, B = 4; switch (x) { case A: ... case B: ... } there isn't much of a clue that `case A` is not `case 3`, which matters more and more the larger the program gets.
This case is comparable to variable shadowing and should then be handled the same way. btw, as of now, what will happen in the case ``` enum ENUM { A, B, C } ENUM x = ENUM.A; enum A = 3, B = 4; switch (x) with (ENUM) { case A: writeln("case A=", cast(int)A);break; case B: writeln("case B=", cast(int)B);break; default:writeln("whatever x=", cast(int)x); } ``` https://run.dlang.io/is/7Ar7dQ it will use the values defined in ENUM and ignore the `enum A` and `enum B` values. The compiler should at least warn of the ambiguity if the `with(ENUM)` is removed.
 One thing I've learned with D is that programmers often have 
 mental models of how symbol lookup is done that diverge 
 substantially from how it actually works, and I've never been 
 able to communicate how things actually work to them. Putting a 
 special case in like this is pretty risky.
Jul 22 2021
prev sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 22 July 2021 at 04:37:43 UTC, Walter Bright wrote:
 On 7/21/2021 8:47 PM, Mathias LANG wrote:
 What I meant was that instead of recommending our users to use 
 `switch (value) with `(EnumType)`, we could just make it the 
 default since it's the common case with enums. Unless we think 
 that requiring the type of the enum be spelt out is better.
 
 It could add to the long list of those usability enhancements 
 that are a hallmark of D (like `_` in number literals). I 
 personally don't mind the verbosity, but I heard this 
 complaint a few times over the years.
Ok, I see what you mean. If it was restricted to the case value expressions, there is merit to the idea. But suppose: enum A = 3, B = 4; switch (x) { case A: ... case B: ... } there isn't much of a clue that `case A` is not `case 3`, which matters more and more the larger the program gets.
Currently, that's what happens ``` enum ENUM { A, B, C } ENUM x = ENUM.A; enum A = 3, B = 4; switch (x) /*with (ENUM)*/ { case A: writeln("case A=", cast(int)A);break; case B: writeln("case B=", cast(int)B);break; default:writeln("whatever x=", cast(int)x); } ``` onlineapp.d(11): Error: cannot implicitly convert expression `3` of type `int` to `ENUM` onlineapp.d(12): Error: cannot implicitly convert expression `4` of type `int` to `ENUM` onlineapp.d(12): Error: duplicate `case 4` in `switch` statement https://run.dlang.io/is/a12yVL The compiler will already do the right thing as it cannot convert the `enum A` and `enum B` to the type of x.
 One thing I've learned with D is that programmers often have 
 mental models of how symbol lookup is done that diverge 
 substantially from how it actually works, and I've never been 
 able to communicate how things actually work to them. Putting a 
 special case in like this is pretty risky.
Jul 22 2021
parent reply Basile B. <b2.temp gmx.com> writes:
On Thursday, 22 July 2021 at 07:42:33 UTC, Patrick Schluter wrote:
 https://run.dlang.io/is/a12yVL

 The compiler will already do the right thing as it cannot 
 convert the `enum A` and `enum B` to the type of x.
Sorry for the massive disapointment but it can : https://run.dlang.io/is/ktKQPT.
Jul 22 2021
next sibling parent reply claptrap <clap trap.com> writes:
On Thursday, 22 July 2021 at 07:49:23 UTC, Basile B. wrote:
 On Thursday, 22 July 2021 at 07:42:33 UTC, Patrick Schluter 
 wrote:
 https://run.dlang.io/is/a12yVL

 The compiler will already do the right thing as it cannot 
 convert the `enum A` and `enum B` to the type of x.
Sorry for the massive disapointment but it can : https://run.dlang.io/is/ktKQPT.
Sometimes i think the main problem with D is all these weird corner cases pop up and people dont say oh look we need to fix this, nail it down, make it consistent, they say or look cool a new feature! Not directed at you fwiw.
Jul 22 2021
parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/22/2021 1:37 AM, claptrap wrote:
 Sometimes i think the main problem with D is all these weird corner cases pop
up 
 and people dont say oh look we need to fix this, nail it down, make it 
 consistent, they say or look cool a new feature!
Programming languages aren't completely consistent because: 1. people don't want them to be 2. what the software must interact with isn't consistent 3. the CPU is not consistent This whole thread is about adding an inconsistent feature.
Jul 22 2021
prev sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 22 July 2021 at 07:49:23 UTC, Basile B. wrote:
 On Thursday, 22 July 2021 at 07:42:33 UTC, Patrick Schluter 
 wrote:
 https://run.dlang.io/is/a12yVL

 The compiler will already do the right thing as it cannot 
 convert the `enum A` and `enum B` to the type of x.
Sorry for the massive disapointment but it can : https://run.dlang.io/is/ktKQPT.
```d - enum ENUM { A, B, C } + enum ENUM : ubyte { A, B, C } ``` You should have put your change also on the forum so that everyone can see. Yes, that's indeed a problem but IMHO it's a bug due to int promotion rule.
Jul 22 2021
next sibling parent claptrap <clap trap.com> writes:
On Thursday, 22 July 2021 at 08:41:55 UTC, Patrick Schluter wrote:
 On Thursday, 22 July 2021 at 07:49:23 UTC, Basile B. wrote:


 Yes, that's indeed a problem but IMHO it's a bug due to int 
 promotion rule.
Its not a bug it's a feature!
Jul 22 2021
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Thursday, 22 July 2021 at 08:41:55 UTC, Patrick Schluter wrote:
 On Thursday, 22 July 2021 at 07:49:23 UTC, Basile B. wrote:
 On Thursday, 22 July 2021 at 07:42:33 UTC, Patrick Schluter 
 wrote:
 https://run.dlang.io/is/a12yVL

 The compiler will already do the right thing as it cannot 
 convert the `enum A` and `enum B` to the type of x.
Sorry for the massive disapointment but it can : https://run.dlang.io/is/ktKQPT.
```d - enum ENUM { A, B, C } + enum ENUM : ubyte { A, B, C } ``` You should have put your change also on the forum so that everyone can see. Yes, that's indeed a problem but IMHO it's a bug due to int promotion rule.
Yes it is definitively a bug, I know it already but I could not resist to recall to the community that it exists, as your example matched _perfectly_. sorry ;)
Jul 22 2021
next sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 22 July 2021 at 08:51:12 UTC, Basile B. wrote:
 On Thursday, 22 July 2021 at 08:41:55 UTC, Patrick Schluter 
 wrote:
 On Thursday, 22 July 2021 at 07:49:23 UTC, Basile B. wrote:
 On Thursday, 22 July 2021 at 07:42:33 UTC, Patrick Schluter 
 wrote:
 [...]
Sorry for the massive disapointment but it can : https://run.dlang.io/is/ktKQPT.
```d - enum ENUM { A, B, C } + enum ENUM : ubyte { A, B, C } ``` You should have put your change also on the forum so that everyone can see. Yes, that's indeed a problem but IMHO it's a bug due to int promotion rule.
Yes it is definitively a bug, I know it already but I could not resist to recall to the community that it exists, as your example matched _perfectly_. sorry ;)
Don't be sorry when what you say is true. :-)
Jul 22 2021
prev sibling parent Basile B. <b2.temp gmx.com> writes:
On Thursday, 22 July 2021 at 08:51:12 UTC, Basile B. wrote:
 On Thursday, 22 July 2021 at 08:41:55 UTC, Patrick Schluter
 Yes, that's indeed a problem but IMHO it's a bug due to int 
 promotion rule.
Yes it is definitively a bug, I know it already but I could not resist to recall to the community that it exists, as your example matched _perfectly_. sorry ;)
bugzilla: https://issues.dlang.org/show_bug.cgi?id=6226 https://issues.dlang.org/show_bug.cgi?id=21460
Jul 22 2021
prev sibling next sibling parent reply zjh <fqbqrr 163.com> writes:
On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 Hello
And the default constructor, Why can't it like `C++`: `T t;` That's all. Many C++ programmers want to have constructors like `C++` as `T t{...};`.
Jul 20 2021
parent reply russhy <russhy gmail.com> writes:
On Wednesday, 21 July 2021 at 06:12:20 UTC, zjh wrote:
 On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 Hello
And the default constructor, Why can't it like `C++`: `T t;` That's all. Many C++ programmers want to have constructors like `C++` as `T t{...};`.
yes that too.. if you can create DIP draft for it and post it on the forum too? se we can have discussion about that too
Jul 21 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 12:08 PM, russhy wrote:
 On Wednesday, 21 July 2021 at 06:12:20 UTC, zjh wrote:
 On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 Hello
And the default constructor, Why can't it like `C++`: `T t;` That's all. Many C++ programmers want to have constructors like `C++` as `T t{...};`.
yes that too.. if you can create DIP draft for it and post it on the forum too? se we can have discussion about that too
It's been discussed many times. It comes up once a year or so.
Jul 21 2021
parent reply zjh <fqbqrr 163.com> writes:
On Wednesday, 21 July 2021 at 21:04:56 UTC, Walter Bright wrote:
 On 7/21/2021 12:08 PM, russhy wrote:
 And the default constructor,
 Why can't it like `C++`: `T t;` That's all.
 Many C++ programmers want to have constructors like `C++` as 
 `T t{...};`.
 It's been discussed many times. It comes up once a year or so.
I really don't understand. Isn't `d` called `pragmatism`? That's so much a little bit of improvement, d can attract a bunch of c++ people, and I don't know `why not to do it`? Some people say that when using `d`, the friction force is small. Since this is an advantage, why not `strengthen` it? For the so-called `consistency`? Are you not against what is called `consistency`?
Jul 21 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 22 July 2021 at 01:12:40 UTC, zjh wrote:
 On Wednesday, 21 July 2021 at 21:04:56 UTC, Walter Bright wrote:
 On 7/21/2021 12:08 PM, russhy wrote:
 And the default constructor,
 Why can't it like `C++`: `T t;` That's all.
 Many C++ programmers want to have constructors like `C++` as 
 `T t{...};`.
 It's been discussed many times. It comes up once a year or so.
I really don't understand. Isn't `d` called `pragmatism`? That's so much a little bit of improvement, d can attract a bunch of c++ people, and I don't know `why not to do it`? Some people say that when using `d`, the friction force is small. Since this is an advantage, why not `strengthen` it? For the so-called `consistency`? Are you not against what is called `consistency`?
Initialization in C++ is described as a "nightmare" by experienced C++ programmers [1]. This is probably not something that D would benefit from imitating. [1] https://www.youtube.com/watch?v=7DTlWPgX6zs
Jul 21 2021
parent reply zjh <fqbqrr 163.com> writes:
On Thursday, 22 July 2021 at 01:40:07 UTC, Paul Backus wrote:
 On Thursday, 22 July 2021 at 01:12:40 UTC, zjh wrote:
 On Wednesday, 21 July 2021 at 21:04:56 UTC, Walter Bright
I am also `experienced ` C++ Programmer,why I don't hurt from it?
Jul 21 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 7:12 PM, zjh wrote:
 I am also `experienced ` C++ Programmer,why I don't hurt from it?
There are many ways to experience C++. Anyhow, the reason that default constructors are not allowed is so that default constructing an object will always succeed. This makes for a significant reduction in complexity. For example, default construction then becomes pure and nothrow. Not everyone agrees, of course.
Jul 21 2021
parent reply zjh <fqbqrr 163.com> writes:
On Thursday, 22 July 2021 at 04:26:45 UTC, Walter Bright wrote:
 On 7/21/2021 7:12 PM, zjh wrote:
 I am also `experienced ` C++ Programmer,why I don't hurt from 
 it?
As a `plain user`, I hope to have the same convenience as `C++`. I know you are very professional. But, the development of `the D language`,Don't you need a lot of `primary users`? IMO, As a language leader, You should provide users with a variety of choices like `C++`. When users feel great, they will naturally let their friends to use `D`. `C++'s` advantage is although It don't do well, but It provide you basic facilities. `D`,the biggest weakness is ecology. Ecology is made up of users. A large number of users may not be as professional as you. So I think `d` should work hard on `small details` to gain users. I've been saying that the biggest advantage of D is `better c++`, which can attract `C++ programmers`. `C++ programmer`, can help `D` in two places, one is to bring the user, the other is to bring the library which brings the user. Therefore, D should pay more attention to the experience of the `C++ man`, rather than the average force, resulting in nothing.
Jul 21 2021
next sibling parent zjh <fqbqrr 163.com> writes:
On Thursday, 22 July 2021 at 06:12:43 UTC, zjh wrote:
 On Thursday, 22 July 2021 at 04:26:45 UTC, Walter Bright wrote:
 On 7/21/2021 7:12 PM, zjh wrote:
 I am also `experienced ` C++ Programmer,why I don't hurt from 
 it?
Primary users increase, promote the increase of lib writers, increase users, and increase lib writers. Why more users, more people write library, because more people use, more sense of achievement. More libraries, naturally increase users. So the more users, the better. The more users, the more errors found, the more errors corrected, the better experience, and the more users. In this way, it also constitutes a positive cycle. Therefore, we must pay attention to `primary users`.
Jul 21 2021
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 11:12 PM, zjh wrote:
 As a `plain user`, I hope to have the same convenience as `C++`.
 I know you are very professional. But, the development of `the D
language`,Don't 
 you need a lot of `primary users`?
 IMO, As a language leader, You should provide users with a variety of choices 
 like `C++`.
 When users feel great, they will naturally let their friends to use `D`.
 `C++'s` advantage is although It don't do well, but It provide you basic 
 facilities.
 `D`,the biggest weakness is ecology. Ecology is made up of users. A large
number 
 of users may not be as professional as you.
 So I think `d` should work hard on `small details` to gain users.
 I've been saying that the biggest advantage of D is `better c++`, which can 
 attract `C++ programmers`.
 `C++ programmer`, can help `D` in two places, one is to bring the user, the 
 other is to bring the library which brings the user.
 Therefore, D should pay more attention to the experience of the `C++ man`, 
 rather than the average force, resulting in nothing.
One of D's goals is to not have C++ features that cause more problems than they are worth. Of course, this is a judgement call and people have different opinions about it. It's also pretty clear that every feature C++ has, good or bad, has its adherents and champions.
Jul 22 2021
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/20/21 11:50 AM, russhy wrote:
 Hello
 
 I all the time wondered why we always have to be so much verbose with 
 enum, when it's not casting to primitives, it is about repeating their 
 long type name, constantly, all the time
 
 After trying some other languages over the past few years, i discovered 
 in zig you can just ommit the enum type name and just use enums this 
 way: .MY_VALUE
 
 I don't know if that's something that could be supported with D, i am 
 not a compiler dude, so i don't have the answer
 
 Adam on discord mentioned using with(ENUM_TYPE) or just an alias, but i 
 think we go ahead and make it simple
 
 I had prepared a DIP [1], not ready at all, but i wanted to initiate 
 some discussion about that feature
 
 So what do you think? yay? nay? why not?
 
 
 [1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
I enjoy having this type of shortcut in Swift, and wish D had something similar. To give some perspective, look no further than configuration options of std.algorithm: ```d arr.sort!("a < b", .unstable); // instead of SwapStrategy.unstable ``` Clear as the original, and less verbose. In Swift, enums are used extensively, and I don't think they would be if the shortcut syntax wasn't available. And in general, any function parameter enums are cumbersome to write in D. However, using `.enumMember` isn't viable as noted by several people. If I were to propose something, it would be to have a short substitute expression type to lookup the member. Then you can do things like type. It wouldn't be a panacea though, since overloading would make this ambiguous. But assignment/initialization would be unambiguous. Probably not going to fly in D. -Steve
Jul 21 2021
next sibling parent reply russhy <russhy gmail.com> writes:
On Wednesday, 21 July 2021 at 13:36:37 UTC, Steven Schveighoffer 
wrote:
 On 7/20/21 11:50 AM, russhy wrote:
 Hello
 
 I all the time wondered why we always have to be so much 
 verbose with enum, when it's not casting to primitives, it is 
 about repeating their long type name, constantly, all the time
 
 After trying some other languages over the past few years, i 
 discovered in zig you can just ommit the enum type name and 
 just use enums this way: .MY_VALUE
 
 I don't know if that's something that could be supported with 
 D, i am not a compiler dude, so i don't have the answer
 
 Adam on discord mentioned using with(ENUM_TYPE) or just an 
 alias, but i think we go ahead and make it simple
 
 I had prepared a DIP [1], not ready at all, but i wanted to 
 initiate some discussion about that feature
 
 So what do you think? yay? nay? why not?
 
 
 [1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
I enjoy having this type of shortcut in Swift, and wish D had something similar. To give some perspective, look no further than configuration options of std.algorithm: ```d arr.sort!("a < b", .unstable); // instead of SwapStrategy.unstable ``` Clear as the original, and less verbose. In Swift, enums are used extensively, and I don't think they would be if the shortcut syntax wasn't available. And in general, any function parameter enums are cumbersome to write in D. However, using `.enumMember` isn't viable as noted by several people. If I were to propose something, it would be to have a short then uses the expression type to lookup the member. Then you return an instance of the type. It wouldn't be a panacea though, since overloading would make this ambiguous. But assignment/initialization would be unambiguous. Probably not going to fly in D. -Steve
of course the . syntax isn't final, the point of the DIP discussion was to discuss about the ability to omit the enum type name, the syntax can always be something else
Jul 21 2021
parent russhy <russhy gmail.com> writes:
of course the . syntax isn't final, the point of the DIP 
discussion was to discuss about the ability to omit the enum type 
name, the syntax can always be something else

edit: quoting is kind hard to follow in the text editor here, i 
made a mistake on the reply above by typing inside the quote
Jul 21 2021
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 6:36 AM, Steven Schveighoffer wrote:
 I enjoy having this type of shortcut in Swift, and wish D had something
similar.
Your wish has been heard and duly granted. The internet delivers once again! See Patrick Schluter and Mike Parker's solutions upthread.
Jul 21 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/21/21 5:02 PM, Walter Bright wrote:
 On 7/21/2021 6:36 AM, Steven Schveighoffer wrote:
 I enjoy having this type of shortcut in Swift, and wish D had 
 something similar.
Your wish has been heard and duly granted. The internet delivers once again! See Patrick Schluter and Mike Parker's solutions upthread.
No, `mixin something; thenCallFunction(...);` isn't even close to the same. -Steve
Jul 21 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/21/2021 2:16 PM, Steven Schveighoffer wrote:
 No, `mixin something; thenCallFunction(...);` isn't even close to the same.
I don't know what it is you're asking for, then.
Jul 21 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/21/21 7:59 PM, Walter Bright wrote:
 On 7/21/2021 2:16 PM, Steven Schveighoffer wrote:
 No, `mixin something; thenCallFunction(...);` isn't even close to the 
 same.
I don't know what it is you're asking for, then.
I'm not asking for anything. What I'm saying is the feature in Swift (called "dot syntax") that allows one to use `.enumMember` wherever a specific enum is required, is not the same as creating symbol aliases for all the enum members in your namespace (or in module namespace). The Swift feature is contextual based on the expression *type*, and doesn't require any extra machinery around it. This means that you don't have weird ambiguities when you have multiple enums as parameters, or names that are local that might override the names. e.g.: ```d enum A { one, two, three } enum B { one, two, three } void foo(A a, B b) {... } // how to call foo without having to specify `A.one` or `B.one`? ``` In Swift, `.symbol` is specifically for type properties, and fit perfectly for enums (and other things, see [here](https://www.swiftbysundell.com/tips/using-dot-syntax-for-static-properties- nd-initializers/)). It does not have the meaning that D has, so it can get away with this. I don't expect D to implement such a thing, and yes, you can do mixin hacks or `with` statements (though there are drawbacks to both). Just saying `.member` instead of `FullEnumType.member` whenever a `FullEnumType` is required is a nice feature of Swift, and I would like to see something like it in D, but I can live without it. BUT if it were to be implemented, obviously `.symbol` doesn't work. That's why I proposed a shortcut to mean "property of the required type" such as -Steve
Jul 22 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/22/2021 9:16 AM, Steven Schveighoffer wrote:
 ```d
 enum A
 {
     one,
     two,
     three
 }
 
 enum B
 {
     one,
     two,
     three
 }
 
 void foo(A a, B b) {... }
 
 // how to call foo without having to specify `A.one` or `B.one`?
 ```
 
 In Swift, `.symbol` is specifically for type properties, and fit perfectly for 
 enums (and other things, see 
 [here](https://www.swiftbysundell.com/tips/using-dot-syntax-for-static-properties-
nd-initializers/)). 
How does that interact with function overloading? I looked at the Swift reference manual https://docs.swift.org/swift-book/ReferenceManual/Expressions.html and couldn't find answers there. Seems to me there'd be a combinatorial explosion as the resolver will have to re-run the match for all different resolutions for `.identifier`. Even if the compiler does try them all and find a best match, for the user reading the code I doubt it would be straightforward to figure out manually which is the right match. It's already hard enough. With the alias mixin proposed, the answer is simple. Two different enums in the same scope with the same member names will cause a compile time error.
Jul 22 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/22/21 1:19 PM, Walter Bright wrote:
 On 7/22/2021 9:16 AM, Steven Schveighoffer wrote:
 ```d
 enum A
 {
     one,
     two,
     three
 }

 enum B
 {
     one,
     two,
     three
 }

 void foo(A a, B b) {... }

 // how to call foo without having to specify `A.one` or `B.one`?
 ```

 In Swift, `.symbol` is specifically for type properties, and fit 
 perfectly for enums (and other things, see 
 [here](https://www.swiftbysundell.com/tips/using-dot-syntax-for-static-properties-
nd-initializers/)). 
How does that interact with function overloading?
Swift is known for "giving up" on expression inference, so possibly it could lead there. But note that Swift (and objective C) have significant parameter names, which means in many cases you *must* name the parameters when calling. This helps with overloading quite a bit (in fact, in Objective C there was no overloading, as the parameter names were part of the function name). Which is to say, the situation in Swift is definitely different than the situation in D. Overloads are handled differently, and expectations are significantly different. I tried out some stuff: ```swift import Foundation enum Foo { case a case b case c } enum Bar { case a case b } func test(_ : Foo) { print("test a"); } func test(_ : Bar) { print("test b"); } test(.a) // error, ambiguous test(Foo.a) // test a test(Bar.a) // test b test(.c) // test a ``` So it seems to work as one might expect. Since `.c` can only match the first, it's used.
 
 I looked at the Swift reference manual 
 https://docs.swift.org/swift-book/ReferenceManual/Expressions.html and 
 couldn't find answers there.
 
 Seems to me there'd be a combinatorial explosion as the resolver will 
 have to re-run the match for all different resolutions for 
 `.identifier`.
But what is "all" resolutions? A combinatorial explosion of 1 or 2 possibilities isn't bad. Remember, the symbol lookup *requires* a contextual type. For example: ```swift var x = .identifier // Compiler error, no type, even if only one type in scope has a member `identifier` var y : SomeEnum = .identifier // OK var z : SomeEnum z = .identifer // OK, we infer the meaning from the type of z ``` I know D doesn't do this. But it's also not the same as what you are saying.
 Even if the compiler does try them all and find a best 
 match, for the user reading the code I doubt it would be straightforward 
 to figure out manually which is the right match. It's already hard enough.
The identifier comes from the expression target's inferred type. There are still ambiguous cases, where the compiler is going to give up. But the lookup doesn't involve the whole world either. Before you go on arguing that D doesn't do this, and shouldn't, I know. It's not that I'm trying to convince you that it should, I'm just showing what has been requested and how it differs from the "mixin solution".
 
 With the alias mixin proposed, the answer is simple. Two different enums 
 in the same scope with the same member names will cause a compile time 
 error.
The answer is, there is no shortcut, just type out the full enum names. With Swift you do not need to. -Steve
Jul 22 2021
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/22/2021 11:16 AM, Steven Schveighoffer wrote:
 But what is "all" resolutions? A combinatorial explosion of 1 or 2
possibilities 
 isn't bad. Remember, the symbol lookup *requires* a contextual type. For
example:
So it appears that Swift simply gives an error if there's more than one result for .identifier, rather than trying to find a match that fits into the context.
 ```swift
 var x = .identifier // Compiler error, no type, even if only one type in scope 
 has a member `identifier`
 var y : SomeEnum = .identifier  // OK
 var z : SomeEnum
 z = .identifer // OK, we infer the meaning from the type of z
 ```
Are you sure that's how Swift works? Inferring the type of the rvalue from the lvalue? Problems come from that, as in: z = x + y + .identifier; For the rvalue, this becomes "top down" type inference, as opposed to the usual "bottom up" type inference. D is exclusively "bottom up", as it is much simpler.
 It's not
 that I'm trying to convince you that it should, I'm just showing what has been 
 requested and how it differs from the "mixin solution".
Thanks!
Jul 22 2021
next sibling parent Dukc <ajieskola gmail.com> writes:
On Friday, 23 July 2021 at 01:09:31 UTC, Walter Bright wrote:
 Are you sure that's how Swift works? Inferring the type of the 
 rvalue from the lvalue? Problems come from that, as in:

    z = x + y + .identifier;

 For the rvalue, this becomes "top down" type inference, as 
 opposed to the usual "bottom up" type inference. D is 
 exclusively "bottom up", as it is much simpler.
It could work with bottom-up inference only. It would work the same way as `null` did before adding the bottom type: `.identifier` would be a literal of an internal compiler type, let's call it `EnumLiteral!"identifier"`. Values of that internal compiler type would implicitly convert to any enumerated type that defines `identifier`.
Jul 23 2021
prev sibling next sibling parent Paul Backus <snarwin gmail.com> writes:
On Friday, 23 July 2021 at 01:09:31 UTC, Walter Bright wrote:
 For the rvalue, this becomes "top down" type inference, as 
 opposed to the usual "bottom up" type inference. D is 
 exclusively "bottom up", as it is much simpler.
D is mostly "bottom up", but there are a few corner cases [1] where it isn't, and they can lead to some very confusing errors [2]. It would probably be too disruptive to deprecate existing instances of "top down" type inference in D, but we can at least avoid adding new ones. [1] https://forum.dlang.org/post/mnxksewxtrwlxeaazvwe forum.dlang.org [2] https://forum.dlang.org/thread/xkgpheuhohbffzgdbqhb forum.dlang.org
Jul 23 2021
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Friday, 23 July 2021 at 01:09:31 UTC, Walter Bright wrote:
 Are you sure that's how Swift works? Inferring the type of the 
 rvalue from the lvalue? Problems come from that, as in:

    z = x + y + .identifier;

 For the rvalue, this becomes "top down" type inference, as 
 opposed to the usual "bottom up" type inference. D is 
 exclusively "bottom up", as it is much simpler.
I don't disagree. I think in your example here, it's not inferring the requested type from z, but would be from the operator overload function. There are two requirements here: 1. The type is inferred before the property is looked up (or at least, the set of possible types). Nearly everything in Swift is inferred type. 2. The `.identifier` is a self-producing property based on the contextual type. That is, it is a property on the expected/inferred type, AND it produces an instance of the type. It helps to remember also that Swift's enums are much more configurable than D's enums -- they are actual enumerations, and allow you to define properties, operators, etc. They are basically a specialized struct (so for instance, you can identify whether math is usable on them). -Steve
Jul 23 2021
prev sibling next sibling parent reply Dukc <ajieskola gmail.com> writes:
On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 So what do you think? yay? nay? why not?
As mentioned by others, `enumVar = .memberName` is ambiguous with the module scope operator. If you want to pursue this, might I recommend `enumVar = "memberName"` instead? You still need no new syntax, and it could be implemented with value range propagation mechanics. This way: If a `string`, `wstring` or `dstring` `str` that is known at compile time, is assigned to value of enumerated type `ET`, it is interpreted as assigning `mixin("ET." ~ str)` to the assignee value.
Jul 21 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 22 July 2021 at 03:21:06 UTC, Dukc wrote:
 On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 So what do you think? yay? nay? why not?
As mentioned by others, `enumVar = .memberName` is ambiguous with the module scope operator. If you want to pursue this, might I recommend `enumVar = "memberName"` instead? You still need no new syntax, and it could be implemented with value range propagation mechanics. This way: If a `string`, `wstring` or `dstring` `str` that is known at compile time, is assigned to value of enumerated type `ET`, it is interpreted as assigning `mixin("ET." ~ str)` to the assignee value.
```d enum E : string { x = "y", y = "x" } E e = "y"; // what happens here? ```
Jul 21 2021
parent reply Dukc <ajieskola gmail.com> writes:
On Thursday, 22 July 2021 at 03:28:44 UTC, Paul Backus wrote:
 ```d
 enum E : string { x = "y", y = "x" }

 E e = "y"; // what happens here?
 ```
The VRP mechanics do not currently let that compile, so nothing will break. it would be equal to `E e = E.y`; `E e = cast(E)"y"` on the other hand would still be `E e = E.x`, as it's now. Now, I admit that difference between implicit and explicit cast is confusing and a weakness. Maybe the syntax should be `E e = ".y"` instead to avoid the difference in behaviour between implicit and explicit cast.
Jul 22 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 22 July 2021 at 14:05:53 UTC, Dukc wrote:
 On Thursday, 22 July 2021 at 03:28:44 UTC, Paul Backus wrote:
 ```d
 enum E : string { x = "y", y = "x" }

 E e = "y"; // what happens here?
 ```
The VRP mechanics do not currently let that compile, so nothing will break. it would be equal to `E e = E.y`; `E e = cast(E)"y"` on the other hand would still be `E e = E.x`, as it's now. Now, I admit that difference between implicit and explicit cast is confusing and a weakness. Maybe the syntax should be `E e = ".y"` instead to avoid the difference in behaviour between implicit and explicit cast.
Ok, now imagine you're new to D and don't know about this feature. What are you going to *think* `E e = ".y"` does? Using string-literal syntax for things that aren't actually string literals is just asking for confusion.
Jul 22 2021
next sibling parent Dukc <ajieskola gmail.com> writes:
On Thursday, 22 July 2021 at 14:58:12 UTC, Paul Backus wrote:
 Ok, now imagine you're new to D and don't know about this 
 feature. What are you going to *think* `E e = ".y"` does?
There would be a risk that I'd think that it's the same as `E e = .y`. Not that likely though - I may well know that E has a member y, and I could also reason that `".y"` meaning `.y` would make no sense since one could just write `.y` directly. I think you're worried about the case where I don't know that `E` is an enumerated type. In that case I could mistake `E` being some sort of string type. On the other hand, I could not fully trust that conclusion anyway, since it could also be some custom `struct` or `union` that can be initialized that way.
 Using string-literal syntax for things that aren't actually 
 string literals is just asking for confusion.
To some degree yes. But perhaps it's still better than new syntax.
Jul 22 2021
prev sibling parent Dukc <ajieskola gmail.com> writes:
On Thursday, 22 July 2021 at 14:58:12 UTC, Paul Backus wrote:
 Ok, now imagine you're new to D and don't know about this 
 feature. What are you going to *think* `E e = ".y"` does?
Oh, I just realized adding the dot solves nothing. ```d enum E : string { x = ".y", y = ".x" } E e = ".y"; // *Facepalm* ```
Jul 22 2021
prev sibling parent bauss <jj_1337 live.dk> writes:
On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:
 Hello

 I all the time wondered why we always have to be so much 
 verbose with enum, when it's not casting to primitives, it is 
 about repeating their long type name, constantly, all the time

 After trying some other languages over the past few years, i 
 discovered in zig you can just ommit the enum type name and 
 just use enums this way: .MY_VALUE

 I don't know if that's something that could be supported with 
 D, i am not a compiler dude, so i don't have the answer

 Adam on discord mentioned using with(ENUM_TYPE) or just an 
 alias, but i think we go ahead and make it simple

 I had prepared a DIP [1], not ready at all, but i wanted to 
 initiate some discussion about that feature

 So what do you think? yay? nay? why not?


 [1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md
Terrible, just terrible.
Jul 22 2021