digitalmars.D - DIPX: Enum Literals / Implicit Selector Expression
- russhy (16/16) Dec 02 2021 # DIPX: Enum Literals / Implicit Selector Expression
- Dennis (38/40) Dec 02 2021 I love enums and think they could use some more love in D, but
- Paul Backus (49/52) Dec 02 2021 First impressions:
- Dave P. (14/34) Dec 02 2021 Where this really shines are with bit flags. Enabling this would
- Elronnd (5/6) Dec 02 2021 Bitflags are a case where 'with' _does_ work.
- Dave P. (22/28) Dec 02 2021 Except that `with` introduces a new scope and can’t be used at
- Elronnd (4/5) Dec 02 2021 with-expressions have been suggested. I think they would be a
- bauss (9/39) Dec 03 2021 You could do this however:
- bauss (7/51) Dec 03 2021 Or even:
- russhy (7/13) Dec 03 2021 the proposal is to specifically avoid having to be this repetitive
- Nick Treleaven (7/15) Dec 03 2021 There was a proposal (with an implementation) to allow `with`
- user1234 (5/8) Dec 03 2021 No, that does not work with variable initializers. That damn
- Rumbu (20/24) Dec 02 2021 In C# you can bring all enum members into scope with `using
- Dave P. (11/25) Dec 03 2021 The problem with this solution (and other `with` based solutions)
- Ogi (14/15) Jun 30 2022 I was always wondering why this doesn’t “just work” in D:
- bauss (33/34) Jun 30 2022 ```d
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (3/7) Jun 30 2022 C++ solves this with `enum class` for stricter enumeration
- bauss (4/11) Jun 30 2022 I think that again depends on what one expects to happen, as it
- ryuukk_ (12/55) Jun 30 2022 the goal is not to copy c/c++, it's to be smarter, they are not
- Mike Parker (12/23) Jun 30 2022 My problem with this is it is *not* obvious at all to me. When I
- bauss (18/50) Jun 30 2022 What it seems like it's trying to solve is this:
- ryuukk_ (7/61) Jun 30 2022 SomeEnum flags = SomeEnum (a | b | c);
- The Zealot (8/42) Jun 30 2022 and if you later see a line like
- ryuukk_ (7/54) Jun 30 2022 I already explained in earlier replies
- ryuukk_ (21/24) Jun 30 2022 Indeed, this example doesn't make much sense, but the ony i
- bauss (3/27) Jun 30 2022 Meh... if you need to specify that many enum values then perhaps
- ShadoLight (16/30) Jun 30 2022 Where do you want to use it where it cannot be used?
- ryuukk_ (12/47) Jun 30 2022 Now it is harder to see the function, 'with' is nice when you
- Dom Disc (20/22) Jul 01 2022 It's funny that in fact I like this really. It's much more
- ShadoLight (37/55) Jul 01 2022 At the cost of a CR you get:
- ryuukk_ (2/6) Jul 01 2022 Reading this thread, i might be using the wrong language and i
- ryuukk_ (35/70) Jun 30 2022 it's like asnwsering
- Walter Bright (5/7) Jun 30 2022 That is top-down type inference. D is designed to be bottom-up type infe...
- Steven Schveighoffer (71/79) Jul 01 2022 Yes, but this is not exactly a case without precedent e.g.:
- Walter Bright (3/12) Jul 01 2022 That's because the initializer, *after* it is evaluated for its type, ge...
- Steven Schveighoffer (16/29) Jul 01 2022 Not really.
- Walter Bright (2/11) Jul 01 2022 It's casting the elements, not the array.
- Timon Gehr (57/92) Jul 05 2022 An even better analogy:
- Walter Bright (2/3) Jul 06 2022 That example just hurts my brain :-/
- 12345swordy (4/8) Jul 07 2022 Side note: Does D really needs function overloading? You can
- Alexandru Ermicioi (2/5) Jul 07 2022 Just wondering, how do you achieve it using meta-programming?
- Nick Treleaven (13/19) Jul 07 2022 For non-virtual functions, just use a template function:
- Alexandru Ermicioi (4/14) Jul 07 2022 This solution feels like a huge downgrade compared to existing
- 12345swordy (4/25) Jul 08 2022 This can be solved by having a top type named "Any" to indicate
- Paul Backus (4/21) Jul 08 2022 Actually, you can already do this with variadic functions and
- 12345swordy (3/25) Jul 08 2022 "TypeInfo" isn't a Top type though, nor should it be treated as
- Timon Gehr (11/16) Jul 07 2022 Well, I don't recommend people use that specific pattern, but it makes a...
- Walter Bright (16/17) Jul 01 2022 Those are reasonable suggestions.
- Steven Schveighoffer (64/89) Jul 01 2022 This is why I'm asking the questions. I don't have experience on how to
- Walter Bright (16/100) Jul 01 2022 I find this puzzling, because in all my years I have *never* wanted non-...
- Steven Schveighoffer (58/114) Jul 01 2022 These are not unscoped enums. There is a fundamental disconnect with
- Walter Bright (11/47) Jul 04 2022 Of course, that precludes `typeof(a)`, `a.sizeof`, `a + 3`, function tem...
- TheGag96 (3/5) Jul 04 2022 By the `#` in front, right? That makes it a literal, not a plain
- Steven Schveighoffer (37/75) Jul 04 2022 Yes. The point is to avoid having to write `BigLongEnumTypeName`, and
- bauss (6/10) Jul 04 2022 Perhaps : could work as `:value` isn't currently a reserved
- Steven Schveighoffer (5/18) Jul 05 2022 I thought that too. But there is existing syntax that might interfere:
- bauss (5/10) Jul 06 2022 I don't think that would interfere, because `cond ? value1
- Steven Schveighoffer (6/17) Jul 06 2022 Probably fine, I agree. Again, just being cautious. I will stress also
- Timon Gehr (43/60) Jul 05 2022 I have implemented D's pretty simple function overloading rules in my
- Steven Schveighoffer (20/25) Jul 05 2022 I'm concerned about that though, because this brings what should be an
- Timon Gehr (21/54) Jul 05 2022 Then you get the same behavior as with lambdas.
- Steven Schveighoffer (8/35) Jul 05 2022 Yeah, I think this is better, it's what I would expect. But I can see
- TheGag96 (20/21) Jul 03 2022 This is exactly how it should work. I think this is a pretty good
- Walter Bright (4/6) Jun 30 2022 C++ threw in the towel and added scoped enums with a somewhat different ...
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (3/9) Jul 01 2022 C and D don't have an enumeration type. `enum class` gave C++ a
- user1234 (7/9) Jul 01 2022 Absolutely. This is why we can write this insanity:
- Walter Bright (2/4) Jul 01 2022 Indubitably.
- Nick Treleaven (4/14) Jul 02 2022 Isn't that just because a value also has the properties of its
- ryuukk_ (15/15) Jul 02 2022 Just right now i got annoyed with enums again while porting an
- Walter Bright (4/7) Jun 30 2022 It is deliberate, not a bug. The rationale is the common C practice:
- ryuukk_ (9/17) Jul 01 2022 The proposal is not to mimic C, is to make use of the type system
I watched DConf and the idea was suggested, there wasn't any disagreements or negative comments about it, so i figured it's time to start to work on the DIP! That's a feature i long desired, and i'm pretty envious of the languages that have it implemented, to name a few: Zig/Odin/Jai ``with`` is often mentioned as a way to do something similar, but it is not the same as just calling the enum.. plus it pollutes the scope and introduce issues such as symbol/name clashes, this is not what we want Of course the DIP is not complete, it's a start, let's discuss about it, let's improve the DIP and let's find solutions how it could be implemented if we all agree that it would be a nice addition to the language! Link: https://gist.github.com/RUSShyTwo/2fcf6d294d25ba748d61927b5eed2944
Dec 02 2021
I love enums and think they could use some more love in D, but I'm skeptical whether this particular thing can be made to work in D without introducing edge cases and complexity. On Thursday, 2 December 2021 at 19:44:07 UTC, russhy wrote:That's a feature i long desired, and i'm pretty envious of the languages that have it implemented, to name a few: Zig/Odin/JaiDon't know about Odin/Jai, but Zig does not have function overloading / UFCS / template functions, so it doesn't have to deal with D's existing complex baggage. The DIP should figure out what happens in these situations: ```D // ambiguous overload enum A {a} string a() {return "a";} auto f0(A x) {return x;} auto f0(string x) {return x;} pragma(msg, typeof(f0(.a))); // string? ``` ```D // difficult type inference enum A {a, b} enum B {a, b, c} auto f1(T)(T x) {return x;} pragma(msg, typeof(f1(.a))); // ? pragma(msg, typeof([.a, .b, .c])); // B[]? ``` ```D // Subtype member shadowing enum A { a = 1, c = 3, } enum B : A { a = A.c, b = A.a, } immutable A[] arr = [.a, .b]; pragma(msg, arr); // ? ``` There's probably more.
Dec 02 2021
On Thursday, 2 December 2021 at 19:44:07 UTC, russhy wrote:[...]Link: https://gist.github.com/RUSShyTwo/2fcf6d294d25ba748d61927b5eed2944First impressions: ```d enum MyTag { A, B, C } MyTag tag = .A; ``` This looks like it can be made to work very easily, although I am not convinced it is a huge improvement compared to just using `auto`: ```d auto tag = MyTag.A; ``` Meanwhile, this: ```d void what(MyTag tag) {} what(.B); ``` ...is potentially troublesome, because it is grammatically ambiguous with the [module scope operator][1]. For example: ```d enum MyTag { A, B, C } int B = 42; void what(MyTag) {} void example() { what(.B); // Error: function `what` is not callable using argument types `(int)` } ``` We would need some rule like, "if an expression of the form `.Identifier` fails to compile, the compiler will attempt to rewrite it as `EnumName.Identifier`, where `EnumName` is the name of an enum visible in the current scope." This raises several follow-on questions: * What do we do if there are multiple enums with matching members? * What if one of those enums is in the current module, and one is imported? * Do we need a rule to prevent enum members from being "hijacked" by global variables? * When instantiating templates, do we search enum types passed as template parameters, or just enums in the template declaration's lexical scope? [1]: https://dlang.org/spec/module.html#module_scope_operators
Dec 02 2021
On Thursday, 2 December 2021 at 20:38:08 UTC, Paul Backus wrote:On Thursday, 2 December 2021 at 19:44:07 UTC, russhy wrote:Where this really shines are with bit flags. Enabling this would be quite nice: ```d MyFlags flags = .A | .B | .C; functionWithFlags(.Foo | .Bar, .Baz | .Qux); ``` vs. ```d auto flags = MyFlags.A | MyFlags.B | MyFlags.C; functionWithFlags(FuncOption.Foo | FuncOption.Bar, Second.Baz | Second.Qux); ``` It’s unfortunate that the module scope operator is prefix `.`[...][...]First impressions: ```d enum MyTag { A, B, C } MyTag tag = .A; ``` This looks like it can be made to work very easily, although I am not convinced it is a huge improvement compared to just using `auto`: ```d auto tag = MyTag.A; ```
Dec 02 2021
On Friday, 3 December 2021 at 02:38:18 UTC, Dave P. wrote:Where this really shines are with bit flags.Bitflags are a case where 'with' _does_ work. More generally, I'm sceptical of the feature for the reasons Dennis and Paul mention; there is clearly some advantage wrt boilerplate in some cases, but not for bitflags.
Dec 02 2021
On Friday, 3 December 2021 at 04:25:06 UTC, Elronnd wrote:On Friday, 3 December 2021 at 02:38:18 UTC, Dave P. wrote:Except that `with` introduces a new scope and can’t be used at file scope: ```d enum Flags { FOO = 1, BAR = 2, BAZ = 4, QUX = 8, } void main(){ with(Flags){ auto flags = FOO | BAR | BAZ | QUX; } // flags does not exist here. } with(Flags){ // Illegal auto f = FOO | BAR; } ``` With is also very noisy syntactically when all you want is to use enums in a nicer way.Where this really shines are with bit flags.Bitflags are a case where 'with' _does_ work. More generally, I'm sceptical of the feature for the reasons Dennis and Paul mention; there is clearly some advantage wrt boilerplate in some cases, but not for bitflags.
Dec 02 2021
On Friday, 3 December 2021 at 04:58:51 UTC, Dave P. wrote:`with` introduces a new scope and can’t be used at file scopewith-expressions have been suggested. I think they would be a great idea; independently of present discussion, but also as a feature that doesn't have the obvious problems this does.
Dec 02 2021
On Friday, 3 December 2021 at 04:58:51 UTC, Dave P. wrote:On Friday, 3 December 2021 at 04:25:06 UTC, Elronnd wrote:You could do this however: ```d Flags flags; with(Flags){ flags = FOO | BAR | BAZ | QUX; } // flags does exist here now. ```On Friday, 3 December 2021 at 02:38:18 UTC, Dave P. wrote:Except that `with` introduces a new scope and can’t be used at file scope: ```d enum Flags { FOO = 1, BAR = 2, BAZ = 4, QUX = 8, } void main(){ with(Flags){ auto flags = FOO | BAR | BAZ | QUX; } // flags does not exist here. } with(Flags){ // Illegal auto f = FOO | BAR; } ``` With is also very noisy syntactically when all you want is to use enums in a nicer way.Where this really shines are with bit flags.Bitflags are a case where 'with' _does_ work. More generally, I'm sceptical of the feature for the reasons Dennis and Paul mention; there is clearly some advantage wrt boilerplate in some cases, but not for bitflags.
Dec 03 2021
On Friday, 3 December 2021 at 09:35:47 UTC, bauss wrote:On Friday, 3 December 2021 at 04:58:51 UTC, Dave P. wrote:Or even: ```d Flags flags; with(Flags) flags = FOO | BAR | BAZ | QUX; // flags does exist here now. ```On Friday, 3 December 2021 at 04:25:06 UTC, Elronnd wrote:You could do this however: ```d Flags flags; with(Flags){ flags = FOO | BAR | BAZ | QUX; } // flags does exist here now. ```On Friday, 3 December 2021 at 02:38:18 UTC, Dave P. wrote:Except that `with` introduces a new scope and can’t be used at file scope: ```d enum Flags { FOO = 1, BAR = 2, BAZ = 4, QUX = 8, } void main(){ with(Flags){ auto flags = FOO | BAR | BAZ | QUX; } // flags does not exist here. } with(Flags){ // Illegal auto f = FOO | BAR; } ``` With is also very noisy syntactically when all you want is to use enums in a nicer way.Where this really shines are with bit flags.Bitflags are a case where 'with' _does_ work. More generally, I'm sceptical of the feature for the reasons Dennis and Paul mention; there is clearly some advantage wrt boilerplate in some cases, but not for bitflags.
Dec 03 2021
On Friday, 3 December 2021 at 09:36:46 UTC, bauss wrote:Or even: ```d Flags flags; with(Flags) flags = FOO | BAR | BAZ | QUX; // flags does exist here now. ```the proposal is to specifically avoid having to be this repetitive `Flags flags with Flags flags = ...` This is way too much cognitive load only to get to the point of simply doing ``FOO | BAR | BAZ | QUX`` `with` when you think about using it, when you just want to use the enum it becomes cumbersome
Dec 03 2021
On Friday, 3 December 2021 at 04:58:51 UTC, Dave P. wrote:Except that `with` introduces a new scopeThere was a proposal (with an implementation) to allow `with` without braces: https://forum.dlang.org/post/bwudoaknbiodwilvldxl forum.dlang.organd can’t be used at file scope:That should be made to work.with(Flags){ // Illegal auto f = FOO | BAR; } ``` With is also very noisy syntactically when all you want is to use enums in a nicer way.Fair point, but is there a simple solution to that without ambiguity drawbacks?
Dec 03 2021
On Friday, 3 December 2021 at 04:25:06 UTC, Elronnd wrote:On Friday, 3 December 2021 at 02:38:18 UTC, Dave P. wrote:No, that does not work with variable initializers. That damn variable is then declared in a nested scope. Maybe something like `static with(){}` could be added for those cases but well, that's not the proposal here, let's not fork already.Where this really shines are with bit flags.Bitflags are a case where 'with' _does_ work.
Dec 03 2021
On Thursday, 2 December 2021 at 19:44:07 UTC, russhy wrote:could be implemented if we all agree that it would be a nice addition to the language! Link: https://gist.github.com/RUSShyTwo/2fcf6d294d25ba748d61927b5eed2944static` ```csharp enum MyEnum {A, B, C} using static MyEnum; MyEnum a = A; ``` Usual language overload resolution applies for conflicts. If there are conflicts, ambiguity error is rendered by the compiler. Even there are conflicts, the compiler is smart enough to know when you are referring to `MyEnum` when a variable is typed accordingly. An equivalent D idiom will be ```d static alias MyEnum; ``` Currently there is a proposal champion for the dot notation also https://github.com/dotnet/csharplang/issues/2926
Dec 02 2021
On Friday, 3 December 2021 at 05:41:20 UTC, Rumbu wrote:On Thursday, 2 December 2021 at 19:44:07 UTC, russhy wrote:The problem with this solution (and other `with` based solutions) is that it is effectively undoing a nice feature of D of sane namespaces. If you use a class or a struct, you don’t have to explicitly dump all of its member functions into your current scope. You just look up the symbol in the namespace of the struct/class. Similarly, a benefit of this proposal and how it works in Jai/Zig/Odin is that the compiler already knows what type it is expecting and so you are just looking up the name in the namespace of that type. No namespace pollution and it is very convenient.could be implemented if we all agree that it would be a nice addition to the language! Link: https://gist.github.com/RUSShyTwo/2fcf6d294d25ba748d61927b5eed2944static` [...] An equivalent D idiom will be ```d static alias MyEnum; ``` [...]
Dec 03 2021
I tried to implement this, i spent the whole day today on it, unfortunately, i'm unable to make progress, to be honest, it was like diving in an endless ocean, i was clueless lol, if anyone is willing to help, that would be super cool, that feature deserve to be implemented! Here is a test code: ```D --- app.d import data; void main() { pass_enum(MyEnum.MyValueB); pass_enum(.MyValueA); } --- data.d module data; enum MyEnum { MyValueA, MyValueB } void pass_enum(MyEnum e) {} ``` The idea is: 1- call ``pass_enum`` with ``.MyValueA`` 2- dmd should be able to know what symbol it refers to in the pass_enum function 3- dmd should check in all the known modules if he can find that symbol 4- if dmd found the symbol, check its members and compare both values 5- if it finds it, then use that symbol 6- if it doesn't find it, then fall back to the old behavior of checking in the module for values I'm clueless about both DMD and language development, so that adds another layer of difficulty for me, trying to understand DMD code base, and overall how language and compiler works, i still have no clue how everything world.. but i still managed to get somewhere I put an ``else`` here: https://github.com/dlang/dmd/blob/6f2a4daebabe666fbb5d9fde1aa6a0e6ddb6b70d/src/dmd/expressionsem.d#L12686 So the compiler doesn't complain about ``undefined identifier``, this allows me to start working on step 2, and that's where i got stuck.. If anyone is willing to help me, that would be sweet
Jun 25 2022
On Saturday, 25 June 2022 at 16:10:03 UTC, ryuukk_ wrote:I tried to implement this, i spent the whole day today on it, unfortunately, i'm unable to make progress, [...] If anyone is willing to help me, that would be sweetI've implemented something similar in styx, it's called "parent enum inference" but I dont know if it can be done the same way in DMD. The architecture of the compiler is only partially similar. I'll describe how this work for me, hope this'll help ;) So in styx this works almost like a "WithStatement": A. Scope (Something that can be done very similarly in DMD) When it is wished to inferer enum parent names, I push a scope. In this scope I add as member the equivalent of a "with" expression, That exp gives, as an IdentExp, the enum. In my case I add really the same data type as with a WithStatement, but there's a flag used to distinguish expressions used for "with" and those used for "parent enum inference". This is because in styx there is no special syntax (in opposition to the leading dot proposed for what you try to implement), it is just specified that this is tried at the very end. Finally when it is not required to infer anymore, pop the scope. related code: - https://gitlab.com/styx-lang/styx/-/blob/master/src/styx/semantic/declarations.sx#L147 - https://gitlab.com/styx-lang/styx/-/blob/master/src/styx/scope.sx#L24 example use, when the switched expression is an enum and to allow inference on the cases: - https://gitlab.com/styx-lang/styx/-/blob/master/src/styx/semantic/statements.sx#L681 B. Resolving (That part would be very different in DMD) When an identifier has been unsuccessfully resolved, the expressions added in the scope are tried. - https://gitlab.com/styx-lang/styx/-/blob/master/src/styx/semantic/expressions.sx#L2253
Jun 25 2022
On Saturday, 25 June 2022 at 20:48:15 UTC, Basile B. wrote:On Saturday, 25 June 2022 at 16:10:03 UTC, ryuukk_ wrote:Thanks a lot for your detailed and helpful reply, will give it another try today! Compiler stuff is quite intimidatingI tried to implement this, i spent the whole day today on it, unfortunately, i'm unable to make progress, [...] If anyone is willing to help me, that would be sweetI've implemented something similar in styx, it's called "parent enum inference" but I dont know if it can be done the same way in DMD. The architecture of the compiler is only partially similar. I'll describe how this work for me, hope this'll help ;) So in styx this works almost like a "WithStatement": A. Scope (Something that can be done very similarly in DMD) When it is wished to inferer enum parent names, I push a scope. In this scope I add as member the equivalent of a "with" expression, That exp gives, as an IdentExp, the enum. In my case I add really the same data type as with a WithStatement, but there's a flag used to distinguish expressions used for "with" and those used for "parent enum inference". This is because in styx there is no special syntax (in opposition to the leading dot proposed for what you try to implement), it is just specified that this is tried at the very end. Finally when it is not required to infer anymore, pop the scope. related code: - https://gitlab.com/styx-lang/styx/-/blob/master/src/styx/semantic/declarations.sx#L147 - https://gitlab.com/styx-lang/styx/-/blob/master/src/styx/scope.sx#L24 example use, when the switched expression is an enum and to allow inference on the cases: - https://gitlab.com/styx-lang/styx/-/blob/master/src/styx/semantic/statements.sx#L681 B. Resolving (That part would be very different in DMD) When an identifier has been unsuccessfully resolved, the expressions added in the scope are tried. - https://gitlab.com/styx-lang/styx/-/blob/master/src/styx/semantic/expressions.sx#L2253
Jun 26 2022
I tried.., language development is definitely not my cup of tea, i'm unable to do anything productive, hopefully someone else will be able to make use of what you have done already for Styx Who else is willing to do it?
Jun 26 2022
On Sunday, 26 June 2022 at 15:26:22 UTC, ryuukk_ wrote:I tried.., language development is definitely not my cup of tea, i'm unable to do anything productive, hopefully someone else will be able to make use of what you have done already for Styx Who else is willing to do it?After seeing the SOAC post, i went ahead and created an issue on the github/projects https://github.com/dlang/projects/issues/88 Hopefully someone might pick it up
Jun 29 2022
On Thursday, 2 December 2021 at 19:44:07 UTC, russhy wrote:…I was always wondering why this doesn’t “just work” in D: ```D enum Color { red, orange } Color color = orange; ``` This is a major source of frustration when porting from C, where enum assignment does work like this (but, unlike in D, enum member names must be unique). Usually D compiler is smart enough to evaluate the type of the right-hand of an assignment. But for some reason it’s not the case with enums. I was under impression that this is a deliberate (although questionable) design to avoid some bugs rather some technical limitation.
Jun 30 2022
On Thursday, 30 June 2022 at 09:08:30 UTC, Ogi wrote:... But for some reason it’s not the case with enums. ...```d enum Color { red, orange = 3 } int orange = 1; Color color = orange; // what is color? // or what about auto color = orange; // What is color? // or this: auto color = cast(int)orange; // What is color? ``` Arguably each of these can be defined behavior and can be solved, but what defined behavior is correct depends entirely on what people expect to be true, and that's very different depending on the type of project(s) people work on, what language background they come from etc. For me the first one will be 1, because I believe a defined value should override an enum value if specified, but this may be different from others. The second one is the same, the type should be int and not Color. The third one has the type int as well and will obviously use the int variable named orange in my expectation. However I can see the argument for other people wanting the first one to be a compiler error because it's ambiguous, or even that you cannot override enum values and thus it will take the value from Color. All of these behavior are correct and neither is wrong, as it all just depends on how one think about programming in terms of types, values, rules etc. I don't think D could ever settle on a single rule here that a majority would agree upon.
Jun 30 2022
On Thursday, 30 June 2022 at 09:19:15 UTC, bauss wrote:However I can see the argument for other people wanting the first one to be a compiler error because it's ambiguous, or even that you cannot override enum values and thus it will take the value from Color.C++ solves this with `enum class` for stricter enumeration semantics.
Jun 30 2022
On Thursday, 30 June 2022 at 09:50:34 UTC, Ola Fosheim Grøstad wrote:On Thursday, 30 June 2022 at 09:19:15 UTC, bauss wrote:I think that again depends on what one expects to happen, as it only solves it if that's the perceived correct behavior.However I can see the argument for other people wanting the first one to be a compiler error because it's ambiguous, or even that you cannot override enum values and thus it will take the value from Color.C++ solves this with `enum class` for stricter enumeration semantics.
Jun 30 2022
On Thursday, 30 June 2022 at 09:19:15 UTC, bauss wrote:On Thursday, 30 June 2022 at 09:08:30 UTC, Ogi wrote:the goal is not to copy c/c++, it's to be smarter, they are not globals... But for some reason it’s not the case with enums. ...```d enum Color { red, orange = 3 } int orange = 1; Color color = orange; // what is color? // or what about auto color = orange; // What is color? // or this: auto color = cast(int)orange; // What is color? ``` Arguably each of these can be defined behavior and can be solved, but what defined behavior is correct depends entirely on what people expect to be true, and that's very different depending on the type of project(s) people work on, what language background they come from etc. For me the first one will be 1, because I believe a defined value should override an enum value if specified, but this may be different from others. The second one is the same, the type should be int and not Color. The third one has the type int as well and will obviously use the int variable named orange in my expectation. However I can see the argument for other people wanting the first one to be a compiler error because it's ambiguous, or even that you cannot override enum values and thus it will take the value from Color. All of these behavior are correct and neither is wrong, as it all just depends on how one think about programming in terms of types, values, rules etc. I don't think D could ever settle on a single rule here that a majority would agree upon.enum Color { red, orange = 3 }int orange = 1;Color color = orange; // what is color?it's not ``.orange`` therefore it is your variable, it if were with the dot, it'd take what ever is in the enumauto color = orange; // What is color?same, it's not ``.orange`` therefore it's 1, if it where with the dot, it's compile error telling you to be more explicitauto color = cast(int)orange; // What is color?it's not ``.orange`` therefore it is your variable, it if were with the dot, it'd take what ever is in the enum, then apply the cast I don't know it seems pretty obvious to me This feature is too good to not have
Jun 30 2022
On Thursday, 30 June 2022 at 10:51:31 UTC, ryuukk_ wrote:My problem with this is it is *not* obvious at all to me. When I see `.foo`, I immediately think "foo is in the parent scope", as that what is what it means right now in D. If we're suddenly seeing `.foo` at module scope, now I've got a fault in my wiring; `.foo` suddenly has different meanings depending on the scope in which it's used. I mean, maybe I could get used to it if I used it. But I don't see myself using it at all. Not saying I'm against the feature, I just don't understand why `auto color = Color.orange` is a problem and how `Color color = .orange` is better. Because of four fewer characters?auto color = orange; // What is color?same, it's not ``.orange`` therefore it's 1, if it where with the dot, it's compile error telling you to be more explicitauto color = cast(int)orange; // What is color?it's not ``.orange`` therefore it is your variable, it if were with the dot, it'd take what ever is in the enum, then apply the cast I don't know it seems pretty obvious to me This feature is too good to not have
Jun 30 2022
On Thursday, 30 June 2022 at 10:58:37 UTC, Mike Parker wrote:On Thursday, 30 June 2022 at 10:51:31 UTC, ryuukk_ wrote:What it seems like it's trying to solve is this: ```d SomeEnum.a | SomeEnum.b | SomeEnum.c ``` So you could instead do: ```d .a | .b | .c ``` But much like you I don't think that's at all clarified, maybe I would settle for something like this: SomeEnum(a | b | c) as that clearly tells you it's SomeEnum and then it's clear you're picking the values from that, so a, b and c will __always__ be from SomeEnum. But personally I don't really like that syntax either as it clashes too much with struct ctors and/or function calls. So I don't have an actual good solution, so I guess in the end I'm not for any changes.My problem with this is it is *not* obvious at all to me. When I see `.foo`, I immediately think "foo is in the parent scope", as that what is what it means right now in D. If we're suddenly seeing `.foo` at module scope, now I've got a fault in my wiring; `.foo` suddenly has different meanings depending on the scope in which it's used. I mean, maybe I could get used to it if I used it. But I don't see myself using it at all. Not saying I'm against the feature, I just don't understand why `auto color = Color.orange` is a problem and how `Color color = .orange` is better. Because of four fewer characters?auto color = orange; // What is color?same, it's not ``.orange`` therefore it's 1, if it where with the dot, it's compile error telling you to be more explicitauto color = cast(int)orange; // What is color?it's not ``.orange`` therefore it is your variable, it if were with the dot, it'd take what ever is in the enum, then apply the cast I don't know it seems pretty obvious to me This feature is too good to not have
Jun 30 2022
On Thursday, 30 June 2022 at 11:03:08 UTC, bauss wrote:On Thursday, 30 June 2022 at 10:58:37 UTC, Mike Parker wrote:SomeEnum flags = SomeEnum (a | b | c); Why do i have to be repetitive? the goal is to not be repetitive and be cleaner SomeEnum flags = .a | .b | .c; The brain no longer have to read 2x the same stuff, it goes straight to the point, it is explicit and typecheckedOn Thursday, 30 June 2022 at 10:51:31 UTC, ryuukk_ wrote:What it seems like it's trying to solve is this: ```d SomeEnum.a | SomeEnum.b | SomeEnum.c ``` So you could instead do: ```d .a | .b | .c ``` But much like you I don't think that's at all clarified, maybe I would settle for something like this: SomeEnum(a | b | c) as that clearly tells you it's SomeEnum and then it's clear you're picking the values from that, so a, b and c will __always__ be from SomeEnum. But personally I don't really like that syntax either as it clashes too much with struct ctors and/or function calls. So I don't have an actual good solution, so I guess in the end I'm not for any changes.My problem with this is it is *not* obvious at all to me. When I see `.foo`, I immediately think "foo is in the parent scope", as that what is what it means right now in D. If we're suddenly seeing `.foo` at module scope, now I've got a fault in my wiring; `.foo` suddenly has different meanings depending on the scope in which it's used. I mean, maybe I could get used to it if I used it. But I don't see myself using it at all. Not saying I'm against the feature, I just don't understand why `auto color = Color.orange` is a problem and how `Color color = .orange` is better. Because of four fewer characters?auto color = orange; // What is color?same, it's not ``.orange`` therefore it's 1, if it where with the dot, it's compile error telling you to be more explicitauto color = cast(int)orange; // What is color?it's not ``.orange`` therefore it is your variable, it if were with the dot, it'd take what ever is in the enum, then apply the cast I don't know it seems pretty obvious to me This feature is too good to not have
Jun 30 2022
On Thursday, 30 June 2022 at 15:42:05 UTC, ryuukk_ wrote:On Thursday, 30 June 2022 at 11:03:08 UTC, bauss wrote:and if you later see a line like ``` auto flags = .a | .b | .c; ``` imho it's just much harder to figure out whats going on. i agree that a short syntax would be good. i'd prefer _with()_ because it makes it easier to understand later.On Thursday, 30 June 2022 at 10:58:37 UTC, Mike Parker wrote:SomeEnum flags = SomeEnum (a | b | c); Why do i have to be repetitive? the goal is to not be repetitive and be cleaner SomeEnum flags = .a | .b | .c; The brain no longer have to read 2x the same stuff, it goes straight to the point, it is explicit and typechecked[...]What it seems like it's trying to solve is this: ```d SomeEnum.a | SomeEnum.b | SomeEnum.c ``` So you could instead do: ```d .a | .b | .c ``` But much like you I don't think that's at all clarified, maybe I would settle for something like this: SomeEnum(a | b | c) as that clearly tells you it's SomeEnum and then it's clear you're picking the values from that, so a, b and c will __always__ be from SomeEnum. But personally I don't really like that syntax either as it clashes too much with struct ctors and/or function calls. So I don't have an actual good solution, so I guess in the end I'm not for any changes.
Jun 30 2022
On Thursday, 30 June 2022 at 16:25:35 UTC, The Zealot wrote:On Thursday, 30 June 2022 at 15:42:05 UTC, ryuukk_ wrote:I already explained in earlier replies If you use auto, how can the compiler expect you to use an enum called MyEnum? it doesn't make sense, for that case, it falls back to the current behavior, it searches for the parent scope If no variable found, then it is compile error, undefined symbols a, b, cOn Thursday, 30 June 2022 at 11:03:08 UTC, bauss wrote:and if you later see a line like ``` auto flags = .a | .b | .c; ``` imho it's just much harder to figure out whats going on. i agree that a short syntax would be good. i'd prefer _with()_ because it makes it easier to understand later.On Thursday, 30 June 2022 at 10:58:37 UTC, Mike Parker wrote:SomeEnum flags = SomeEnum (a | b | c); Why do i have to be repetitive? the goal is to not be repetitive and be cleaner SomeEnum flags = .a | .b | .c; The brain no longer have to read 2x the same stuff, it goes straight to the point, it is explicit and typechecked[...]What it seems like it's trying to solve is this: ```d SomeEnum.a | SomeEnum.b | SomeEnum.c ``` So you could instead do: ```d .a | .b | .c ``` But much like you I don't think that's at all clarified, maybe I would settle for something like this: SomeEnum(a | b | c) as that clearly tells you it's SomeEnum and then it's clear you're picking the values from that, so a, b and c will __always__ be from SomeEnum. But personally I don't really like that syntax either as it clashes too much with struct ctors and/or function calls. So I don't have an actual good solution, so I guess in the end I'm not for any changes.
Jun 30 2022
Not saying I'm against the feature, I just don't understand why `auto color = Color.orange` is a problem and how `Color color = .orange` is better. Because of four fewer characters?Indeed, this example doesn't make much sense, but the ony i posted on the github shows exactly why we need it ``` set_my_flag( MySuperLongName.MyFlagA | MySuperLongName.MyFlagB | MySuperLongName.MyFlagC | MySuperLongName.MyFlagD | MySuperLongName.MyFlagE | MySuperLongName.MyFlagF ); ``` vs ``` set_my_flag( .MyFlagA | .MyFlagB | .MyFlagC | .MyFlagD | .MyFlagE | .MyFlagF ); ``` One could argue to use ``with`` But what about places where it can't be used? and it is counter productive, if the goal is to remove repetition, why introduce more? It already is explicit, and checked with the type system I suggest trying languages that have that feature and see how practical, and just better enums feel to use I see myself still relying on constants to emulate enums... in D, because of that, that's not good
Jun 30 2022
On Thursday, 30 June 2022 at 11:32:42 UTC, ryuukk_ wrote:Meh... if you need to specify that many enum values then perhaps you just need a new field with all those values.Not saying I'm against the feature, I just don't understand why `auto color = Color.orange` is a problem and how `Color color = .orange` is better. Because of four fewer characters?Indeed, this example doesn't make much sense, but the ony i posted on the github shows exactly why we need it ``` set_my_flag( MySuperLongName.MyFlagA | MySuperLongName.MyFlagB | MySuperLongName.MyFlagC | MySuperLongName.MyFlagD | MySuperLongName.MyFlagE | MySuperLongName.MyFlagF ); ``` vs ``` set_my_flag( .MyFlagA | .MyFlagB | .MyFlagC | .MyFlagD | .MyFlagE | .MyFlagF ); ``` One could argue to use ``with`` But what about places where it can't be used? and it is counter productive, if the goal is to remove repetition, why introduce more? It already is explicit, and checked with the type system I suggest trying languages that have that feature and see how practical, and just better enums feel to use I see myself still relying on constants to emulate enums... in D, because of that, that's not good
Jun 30 2022
On Thursday, 30 June 2022 at 11:32:42 UTC, ryuukk_ wrote:``` set_my_flag( MySuperLongName.MyFlagA | MySuperLongName.MyFlagB | MySuperLongName.MyFlagC | MySuperLongName.MyFlagD | MySuperLongName.MyFlagE | MySuperLongName.MyFlagF ); ``` vs ``` set_my_flag( .MyFlagA | .MyFlagB | .MyFlagC | .MyFlagD | .MyFlagE | .MyFlagF ); ``` One could argue to use ``with`` But what about places where it can't be used? and it is counter productive, if the goal is to remove repetition, why introduce more?Where do you want to use it where it cannot be used? This works today: ``` with(Color) final switch(color) { case red: writeln("red"); break; case orange: writeln("orange"); } ``` ``` with(MySuperLongName) set_my_flag( MyFlagA | MyFlagB | MyFlagC | MyFlagD | MyFlagE | MyFlagF); ``` More clear at the cost of a few more characters to type. Quite acceptable compromise in my view.
Jun 30 2022
On Thursday, 30 June 2022 at 17:15:46 UTC, ShadoLight wrote:On Thursday, 30 June 2022 at 11:32:42 UTC, ryuukk_ wrote:Now it is harder to see the function, 'with' is nice when you need to pull in a symbol into a scope It is not nice when you just want to call a function with your enum Other languages who have it didn't go with a 'with' for a reason, it adds unecessary noise You don't do: with(int) set_my_int(5 + 6 + 7 + 8 + 9 +10); It kills the intent to make things simpler and logical, functions expect Enum, we give its value Function expect int? we give a value``` set_my_flag( MySuperLongName.MyFlagA | MySuperLongName.MyFlagB | MySuperLongName.MyFlagC | MySuperLongName.MyFlagD | MySuperLongName.MyFlagE | MySuperLongName.MyFlagF ); ``` vs ``` set_my_flag( .MyFlagA | .MyFlagB | .MyFlagC | .MyFlagD | .MyFlagE | .MyFlagF ); ``` One could argue to use ``with`` But what about places where it can't be used? and it is counter productive, if the goal is to remove repetition, why introduce more?Where do you want to use it where it cannot be used? This works today: ``` with(Color) final switch(color) { case red: writeln("red"); break; case orange: writeln("orange"); } ``` ``` with(MySuperLongName) set_my_flag( MyFlagA | MyFlagB | MyFlagC | MyFlagD | MyFlagE | MyFlagF); ``` More clear at the cost of a few more characters to type. Quite acceptable compromise in my view.
Jun 30 2022
On Thursday, 30 June 2022 at 20:32:49 UTC, ryuukk_ wrote:You don't do: with(int) set_my_int(5 + 6 + 7 + 8 + 9 +10);It's funny that in fact I like this really. It's much more readable than casting to the desired type. E.g. you might have wished every literal to be float: with(float) foo(5 + 6 + 7 + 8 + 9 + 10); Unfortunately that doesn't work, so we have to live with foo(cast(float)5 + 6 + 7 + 8 + 9 + 10); or foo(5.0f + 6 + 7 + 8 + 9 + 10); which looks short, but you don't know if the compiler will use float or double or real because of the strange propagation rules. or cast every literal: foo(5.0f + 6.0f + 7.0f + 8.0f + 9.0f + 10.0f); Much worse if you want byte: foo(cast(byte)5 + 6 + 7 + 8 + 250); calls foo with int :-( Here even casting each literal doesn't work, you have to cast the result. I really wish with(byte) foo(5 + 6 + 7 + 8 + 9 + 10); would work.
Jul 01 2022
On Thursday, 30 June 2022 at 20:32:49 UTC, ryuukk_ wrote:On Thursday, 30 June 2022 at 17:15:46 UTC, ShadoLight wrote:At the cost of a CR you get: ``` with(MySuperLongName) set_my_flag( MyFlagA | MyFlagB | MyFlagC | MyFlagD | MyFlagE | MyFlagF); ``` For me it is pretty much equal to visually parsing a UDA on a function. But that is just me.On Thursday, 30 June 2022 at 11:32:42 UTC, ryuukk_ wrote: ``` with(MySuperLongName) set_my_flag( MyFlagA | MyFlagB | MyFlagC | MyFlagD | MyFlagE | MyFlagF); ``` More clear at the cost of a few more characters to type. Quite acceptable compromise in my view.Now it is harder to see the function,It is not nice when you just want to call a function with your enumMaybe, but it is less ambiguous. In your scheme: ``` int MyFlagA = 3; void main() { enum MySuperLongName {MyFlagA, MyFlagB, MyFlagC, MyFlagD, MyFlagE, MyFlagF } //Clearly using the enum ... set_my_flag( .MyFlagA | .MyFlagB | .MyFlagC | .MyFlagD | .MyFlagE | .MyFlagF); //Using the enum or the global ...? set_my_flag( .MyFlagA ); } void set_my_flag(int x){} ```You don't do: with(int) set_my_int(5 + 6 + 7 + 8 + 9 +10); It kills the intent to make things simpler and logical, functions expect Enum, we give its value Function expect int? we give a valueThat is only true because of implicit conversion. If set_my_int function was declared as ```void set_my_int(short x)```, then the ```set_my_int(5 + 6 + 7 + 8 + 9 +10);``` statement will not compile (even though the value passed to x will fit in a short) and you will need to cast to short to shut the compiler up. I would agree with you if we didn't have something like ```with(enumName)```, and needed to add the enumName on every member. But we do have the ```with(enumName)```, which is an acceptable solution to me. Also, the fact that the ```.enumMember``` syntax is conflated with 'parent scope' and can in addition be ambiguous, makes me suspect you will need to find a better syntax to get popular support for this idea.
Jul 01 2022
Also, the fact that the ```.enumMember``` syntax is conflated with 'parent scope' and can in addition be ambiguous, makes me suspect you will need to find a better syntax to get popular support for this idea.Reading this thread, i might be using the wrong language and i might be expecting too much for improvements, that's unfortunate
Jul 01 2022
On Thursday, 30 June 2022 at 09:19:15 UTC, bauss wrote:On Thursday, 30 June 2022 at 09:08:30 UTC, Ogi wrote:it's like asnwsering ```D int resultValue = 42; int resultValue() { return 5; } void main() { writeln(resultValue); } ``` in case of ambiguity, the compiler already smart enough to give an error: ``` onlineapp.d(6): Error: function `onlineapp.resultValue` conflicts with variable `onlineapp.resultValue` at onlineapp.d(4) ``` same could be applied for enums and here clearer example for errors: ```D enum Color { orange } int orange = 5; void main() { auto color = .orange; <--- it obviously pick 5; it can't deduce it is an enum Color color = .orange; <--- it obviously pick orange, it already expect an enum int color = .orange; <--- it obviously pick 5, it already expect an int } ``` No need to overthing it, D is typesafe language, it'll work... But for some reason it’s not the case with enums. ...```d enum Color { red, orange = 3 } int orange = 1; Color color = orange; // what is color? // or what about auto color = orange; // What is color? // or this: auto color = cast(int)orange; // What is color? ``` Arguably each of these can be defined behavior and can be solved, but what defined behavior is correct depends entirely on what people expect to be true, and that's very different depending on the type of project(s) people work on, what language background they come from etc. For me the first one will be 1, because I believe a defined value should override an enum value if specified, but this may be different from others. The second one is the same, the type should be int and not Color. The third one has the type int as well and will obviously use the int variable named orange in my expectation. However I can see the argument for other people wanting the first one to be a compiler error because it's ambiguous, or even that you cannot override enum values and thus it will take the value from Color. All of these behavior are correct and neither is wrong, as it all just depends on how one think about programming in terms of types, values, rules etc. I don't think D could ever settle on a single rule here that a majority would agree upon.
Jun 30 2022
On 6/30/2022 8:41 AM, ryuukk_ wrote:Color color = .orange; <--- it obviously pick orange, it already expect an enumThat is top-down type inference. D is designed to be bottom-up type inference. Because of overloading, having both top-down and bottom-up in play would be very confusing and likely a rich source of confusing bugs. People find overloading confusing enough already.
Jun 30 2022
On 7/1/22 1:28 AM, Walter Bright wrote:On 6/30/2022 8:41 AM, ryuukk_ wrote:Yes, but this is not exactly a case without precedent e.g.: ```d short[] x = [1, 2, 3]; // short[] auto y = [1, 2, 3]; // int[] void foo(short[] arr); foo([1, 2, 3]); // works ``` We do have: ```d auto color = Color.orange; ``` which has no repeats of the Color enum. However, this doesn't scale for bitwise enums: ```d auto mask = Enum.optionA | Enum.optionB; ``` I would love it for function overloading to work, and it's definitely not possible for `.value` to be the syntax since that's already taken. But *something* along these lines would be very nice. How I would design it: 1. We designate a syntax for "enum value", which is a polysemous literal (akin to how `1` is polysemous for `int`, `short`, `byte`, `bool`, etc.) that can be resolved as any `enum` type with that specific member name. For purposes of argument, let's say it's `#value`. 2. If you try to assign it to a specific enum, it gets resolved to that enum's type. e.g. `Color c = #orange;` 3. If you try to use it as a function parameter, it gets resolved only if there is no ambiguity. ```d enum A { a, b } enum B { b, c } void bar(int x); void foo(A a); void foo(B b); foo(#a); // calls foo(A) foo(#b); // error, ambiguous bar(#a); // error, must be used as an enum type ``` 4. If it gets used as a non enum type, or an unknown type, then it's a compiler error. ```d enum A { a, b } auto x = #a; // error int y = #a; // error void foo(T)(T t) {} foo(#a); // error ``` 5. If a `#value` literal is used in an expression with only other `#value` literals that would normally result in the type of an `enum`, the compiler will treat the entire expression as a polysemous literal for any `enum` with all the given names. ```d enum A { a, b, c } enum B { b, c, d } A x = #a | #b; // ok auto y = #a | #b; // error, #expression must be used as an enum A z = #a | #d; // error, d is not a member of A void foo(A a); void foo(B b); foo(#a | #b); // calls foo(A) foo(#b | #c); // error, ambiguous ``` Why does it work? Because the type is not an enum, it's an enum expression without any type until it's used as an enum. Since all the values are compile-time, it shouldn't be a problem to generate the constant at the time the type is resolved. Is this possible? If so, would it be allowed? No sense in working on something that has no chance of acceptance, even if it is sound. I will say, this is one thing from Swift that I really miss in D. -SteveColor color = .orange; <--- it obviously pick orange, it already expect an enumThat is top-down type inference. D is designed to be bottom-up type inference. Because of overloading, having both top-down and bottom-up in play would be very confusing and likely a rich source of confusing bugs. People find overloading confusing enough already.
Jul 01 2022
On 7/1/2022 8:42 AM, Steven Schveighoffer wrote:Yes, but this is not exactly a case without precedent e.g.: ```d short[] x = [1, 2, 3]; // short[] auto y = [1, 2, 3]; // int[] void foo(short[] arr); foo([1, 2, 3]); // works ```That's because the initializer, *after* it is evaluated for its type, gets cast to the type of the lvalue.
Jul 01 2022
On 7/1/22 2:22 PM, Walter Bright wrote:On 7/1/2022 8:42 AM, Steven Schveighoffer wrote:Not really. ```d auto a = [1, 2, 3]; short[] b = [1, 2, 3]; auto c = cast(short[])a; writeln(b); // [1, 2, 3] writeln(c); // [1, 0, 2, 0, 3, 0] ``` casting the literal is not the same as casting a regular value of that type. What I am talking about is a thing that has not yet been given a type, and becomes the type you need if it's a valid use. Just like an array literal -- it's a complex expression that maintains it's polysemous quality. The only difference is, an enum `#value` would not have a default type. -SteveYes, but this is not exactly a case without precedent e.g.: ```d short[] x = [1, 2, 3]; // short[] auto y = [1, 2, 3]; // int[] void foo(short[] arr); foo([1, 2, 3]); // works ```That's because the initializer, *after* it is evaluated for its type, gets cast to the type of the lvalue.
Jul 01 2022
On 7/1/2022 12:20 PM, Steven Schveighoffer wrote:```d auto a = [1, 2, 3]; short[] b = [1, 2, 3]; auto c = cast(short[])a; writeln(b); // [1, 2, 3] writeln(c); // [1, 0, 2, 0, 3, 0] ``` casting the literal is not the same as casting a regular value of that type.It's casting the elements, not the array.
Jul 01 2022
On 7/1/22 21:20, Steven Schveighoffer wrote:On 7/1/22 2:22 PM, Walter Bright wrote:An even better analogy: ```d void main(){ auto dg = x=>x; // error // ... } ``` This lambda does not have a default type, but it will infer the parameter type if one is provided: ```d void main(){ int delegate(int) dg = x=>x; // ok // ... } ``` It can even match two distinct types: ```d void foo(int delegate(int) dg){} void foo(float delegate(float) dg){} void main(){ foo(x=>x); // error, ambiguous // ... } ``` I.e., in terms of type checking, everything has already been figured out and works: ```d import std; alias Poly(T)=T delegate(T=T.init); enum poly(string name)=q{ (x){ static if(is(typeof(typeof(x).%s))) return typeof(x).%s; } }.format(name,name); enum Foo{ a, b, } enum Bar{ b, c, } void foo(Poly!Foo foo){ writeln(foo()); } void foo(Poly!Bar bar){ writeln(bar()); } void main(){ foo(mixin(poly!"a")); // ok, calls first overload // foo(mixin(poly!"b")); // error, matches both foo(mixin(poly!"c")); // ok, calls third overload } ``` So it's just a matter of reusing this logic. How reusable it is I don't know, but I guess it has been debugged before. Therefore, I think the main question that remains is syntax, semantics is a solved problem. (Or more pessimistically, any bugs in semantics are already in the compiler today.)On 7/1/2022 8:42 AM, Steven Schveighoffer wrote:Not really. ```d auto a = [1, 2, 3]; short[] b = [1, 2, 3]; auto c = cast(short[])a; writeln(b); // [1, 2, 3] writeln(c); // [1, 0, 2, 0, 3, 0] ``` casting the literal is not the same as casting a regular value of that type. What I am talking about is a thing that has not yet been given a type, and becomes the type you need if it's a valid use. Just like an array literal -- it's a complex expression that maintains it's polysemous quality. The only difference is, an enum `#value` would not have a default type. -SteveYes, but this is not exactly a case without precedent e.g.: ```d short[] x = [1, 2, 3]; // short[] auto y = [1, 2, 3]; // int[] void foo(short[] arr); foo([1, 2, 3]); // works ```That's because the initializer, *after* it is evaluated for its type, gets cast to the type of the lvalue.
Jul 05 2022
On 7/5/2022 4:19 PM, Timon Gehr wrote:I.e., in terms of type checking, everything has already been figured out and works:That example just hurts my brain :-/
Jul 06 2022
On Wednesday, 6 July 2022 at 18:30:10 UTC, Walter Bright wrote:On 7/5/2022 4:19 PM, Timon Gehr wrote:Side note: Does D really needs function overloading? You can achieve the same effect by using meta-programming. - AlexI.e., in terms of type checking, everything has already been figured out and works:That example just hurts my brain :-/
Jul 07 2022
On Thursday, 7 July 2022 at 13:59:17 UTC, 12345swordy wrote:Side note: Does D really needs function overloading? You can achieve the same effect by using meta-programming. - AlexJust wondering, how do you achieve it using meta-programming?
Jul 07 2022
On Thursday, 7 July 2022 at 15:19:59 UTC, Alexandru Ermicioi wrote:On Thursday, 7 July 2022 at 13:59:17 UTC, 12345swordy wrote:For non-virtual functions, just use a template function: ```d auto f(T)(T v) { static if (is(T : int)) // insert code for f(int) overload else ... } ``` It doesn't work for virtual functions as they can't be templates.Side note: Does D really needs function overloading? You can achieve the same effect by using meta-programming. - AlexJust wondering, how do you achieve it using meta-programming?
Jul 07 2022
On Thursday, 7 July 2022 at 15:51:08 UTC, Nick Treleaven wrote:For non-virtual functions, just use a template function: ```d auto f(T)(T v) { static if (is(T : int)) // insert code for f(int) overload else ... } ```This solution feels like a huge downgrade compared to existing functionality. Reminds me of php or javascript, plus you'd need to handle yourself error handling if arguments don't match.
Jul 07 2022
On Thursday, 7 July 2022 at 15:51:08 UTC, Nick Treleaven wrote:On Thursday, 7 July 2022 at 15:19:59 UTC, Alexandru Ermicioi wrote:This can be solved by having a top type named "Any" to indicate that it can be any type for virtual functions. -AlexOn Thursday, 7 July 2022 at 13:59:17 UTC, 12345swordy wrote:For non-virtual functions, just use a template function: ```d auto f(T)(T v) { static if (is(T : int)) // insert code for f(int) overload else ... } ``` It doesn't work for virtual functions as they can't be templates.Side note: Does D really needs function overloading? You can achieve the same effect by using meta-programming. - AlexJust wondering, how do you achieve it using meta-programming?
Jul 08 2022
On Friday, 8 July 2022 at 13:35:12 UTC, 12345swordy wrote:On Thursday, 7 July 2022 at 15:51:08 UTC, Nick Treleaven wrote:Actually, you can already do this with variadic functions and `TypeInfo`. Take a look at the third example: https://dlang.org/spec/function.html#d_style_variadic_functionsFor non-virtual functions, just use a template function: ```d auto f(T)(T v) { static if (is(T : int)) // insert code for f(int) overload else ... } ``` It doesn't work for virtual functions as they can't be templates.This can be solved by having a top type named "Any" to indicate that it can be any type for virtual functions.
Jul 08 2022
On Friday, 8 July 2022 at 14:11:08 UTC, Paul Backus wrote:On Friday, 8 July 2022 at 13:35:12 UTC, 12345swordy wrote:"TypeInfo" isn't a Top type though, nor should it be treated as one.On Thursday, 7 July 2022 at 15:51:08 UTC, Nick Treleaven wrote:Actually, you can already do this with variadic functions and `TypeInfo`. Take a look at the third example: https://dlang.org/spec/function.html#d_style_variadic_functionsFor non-virtual functions, just use a template function: ```d auto f(T)(T v) { static if (is(T : int)) // insert code for f(int) overload else ... } ``` It doesn't work for virtual functions as they can't be templates.This can be solved by having a top type named "Any" to indicate that it can be any type for virtual functions.
Jul 08 2022
On 7/6/22 20:30, Walter Bright wrote:On 7/5/2022 4:19 PM, Timon Gehr wrote:Well, I don't recommend people use that specific pattern, but it makes a lot of sense that it works in this way. `x=>x` type checks as a `T delegate(T)` for any `T`. `(x){}` does not type check as a `T delegate(T)` for any `T`. The delegate literal is polysemous, and when trying to determine matches, it's instantiated for each potential match. Therefore, if a function accepts a `T delegate(T)`, the lambda can decide with `static if` if there is a match or not, arbitrarily depending on `T`. (Note that all instantiations have to type check, as D does not have SFINAE.)I.e., in terms of type checking, everything has already been figured out and works:That example just hurts my brain :-/
Jul 07 2022
On 7/1/2022 8:42 AM, Steven Schveighoffer wrote:How I would design it:Those are reasonable suggestions. But just let me throw this out. I've heard from many sources that nobody understands C++ function overloading. It is described with pages of detail in the Standard. Even compiler writers only *temporarily* understand it while they are implementing it. How C++ programmers deal with it is they randomly try things until it works. D's overloading is simpler than C++'s. But it gets more complex all the time. For example, named arguments make it more complicated. To add more complexity would take a really really strong benefit. BTW, if you really want a specific case to work: enum A { a, b, c } alias a = A.a; alias b = A.b; alias c = A.c; This could probably be automated with metaprogramming.
Jul 01 2022
On 7/1/22 2:32 PM, Walter Bright wrote:On 7/1/2022 8:42 AM, Steven Schveighoffer wrote:This is why I'm asking the questions. I don't have experience on how to implement function overloading, and I don't know what pitfalls might be introduced from doing something like this.How I would design it:Those are reasonable suggestions. But just let me throw this out. I've heard from many sources that nobody understands C++ function overloading. It is described with pages of detail in the Standard. Even compiler writers only *temporarily* understand it while they are implementing it. How C++ programmers deal with it is they randomly try things until it works.D's overloading is simpler than C++'s. But it gets more complex all the time. For example, named arguments make it more complicated. To add more complexity would take a really really strong benefit.There's 2 questions to answer here: 1) is it possible and feasible? 2) do we want to add to the complexity of the language? Obviously if 1 is false, it's a non-starter. And it looks like you have indirectly answered that it is possible. So on to 2: IMO, I think the language benefits greatly from little syntax tricks like this. `foreach` is a great improvement over `for`. omitting parentheses can cause problems, but in general makes things far more pleasant to write/read. I also have experience using a language (Swift) which has this feature, and it's really really nice, especially when dealing with verbose enums. When using D enums after using Swift, I get the same feeling I do when I compiler knows all the information to make this simple, it just is asking me to jump through a few more hoops than it should. I am genuinely curious why this will add too much to the complexity of overloading. The current rules are: 1. no match 2. match with implicit conversions 3. match with qualifier conversion (if the argument type is qualifier-convertible to the parameter type) 4. exact match That shouldn't change. The next part says: Each argument (including any this reference) is compared against the function's corresponding parameter to determine the match level for that argument. The match level for a function is the worst match level of each of its arguments. OK, so a #value expression would be an "exact match" of any enum type that contains all the value literals in that expression, and a a "no match" for any other situation. The rest of the rules would apply as-is, including partial ordering, etc. Since enums are not related, they should be completely distinct in terms of partial ordering. Am I misunderstanding something?BTW, if you really want a specific case to work: enum A { a, b, c } alias a = A.a; alias b = A.b; alias c = A.c; This could probably be automated with metaprogramming.This doesn't scale well. ```d enum A { a, b, c } alias a = A.a; alias b = A.b; alias c = A.c; void bar(A a) {} void foo() { int a = 5; bar(a); // error; } --- mod2.d: import mod1; int a = 5; void foo() { bar(.a); // error; } ``` And I also have used `with` statements, which *mostly* works, but I've come across some head-scratching problems when naming conflicts occur. -Steve
Jul 01 2022
On 7/1/2022 12:37 PM, Steven Schveighoffer wrote:There's 2 questions to answer here: 1) is it possible and feasible?I don't know.2) do we want to add to the complexity of the language? Obviously if 1 is false, it's a non-starter. And it looks like you have indirectly answered that it is possible. So on to 2: IMO, I think the language benefits greatly from little syntax tricks like this. `foreach` is a great improvement over `for`. omitting parentheses can cause problems, but in general makes things far more pleasant to write/read. I also have experience using a language (Swift) which has this feature, and it's really really nice, especially when dealing with verbose enums.I find this puzzling, because in all my years I have *never* wanted non-scoped enums. C has them, I am not unfamiliar with them. I've also *never* heard this desire in 20 years of D.When using D enums after using Swift, I get the same feeling I do when I use or C++ after experiencing D metaprogramming. It feels like the D compiler knows all the information to make this simple, it just is asking me to jump through a few more hoops than it should. I am genuinely curious why this will add too much to the complexity of overloading. The current rules are: 1. no match 2. match with implicit conversions 3. match with qualifier conversion (if the argument type is qualifier-convertible to the parameter type) 4. exact match That shouldn't change. The next part says: Each argument (including any this reference) is compared against the function's corresponding parameter to determine the match level for that argument. The match level for a function is the worst match level of each of its arguments. OK, so a #value expression would be an "exact match" of any enum type that contains all the value literals in that expression, and a a "no match" for any other situation. The rest of the rules would apply as-is, including partial ordering, etc. Since enums are not related, they should be completely distinct in terms of partial ordering. Am I misunderstanding something?For one thing, you'll have to add all the members of all the enums to the symbol lookup. What if they collide? Now you have an argument that may be a member of an arbitrary collection of different enums - which one do you pick for matching porpoises? If you say "try them all for a best match", now you've got a combinatorial explosion if there are several such parameters. C solves this by not having overloading. But it still has a massive name collision problem, which is why C programmers routinely use the enum identifier as prefix for the member names - essentially a homemade way of scoping them. C++ solved the problem by adding scoped enums.Doesn't your proposal have the same error?BTW, if you really want a specific case to work: enum A { a, b, c } alias a = A.a; alias b = A.b; alias c = A.c; This could probably be automated with metaprogramming.This doesn't scale well. ```d enum A { a, b, c } alias a = A.a; alias b = A.b; alias c = A.c; void bar(A a) {} void foo() { int a = 5; bar(a); // error; }--- mod2.d: import mod1; int a = 5; void foo() { bar(.a); // error; } ``` And I also have used `with` statements, which *mostly* works, but I've come across some head-scratching problems when naming conflicts occur.Naming conflicts are worse with the proposed rules.
Jul 01 2022
On 7/1/22 5:23 PM, Walter Bright wrote:On 7/1/2022 12:37 PM, Steven Schveighoffer wrote:These are not unscoped enums. There is a fundamental disconnect with what I wrote and what you are thinking I wrote. Swift's enums are not actually as loose type-wise as D's. They cannot convert to anything implicitly.I also have experience using a language (Swift) which has this feature, and it's really really nice, especially when dealing with verbose enums.I find this puzzling, because in all my years I have *never* wanted non-scoped enums. C has them, I am not unfamiliar with them. I've also *never* heard this desire in 20 years of D.No, you don't. You only need to resolve the names when you are checking whether a function call matches, or an assignment/operation matches.Am I misunderstanding something?For one thing, you'll have to add all the members of all the enums to the symbol lookup.What if they collide? Now you have an argument that may be a member of an arbitrary collection of different enums - which one do you pick for matching porpoises? If you say "try them all for a best match", now you've got a combinatorial explosion if there are several such parameters.It's a combinatorial explosion *only if* you wrote function overloads for all the possible combinations. Remember, this doesn't (can't) IFTI enum types from #values. It has to be concrete.C solves this by not having overloading. But it still has a massive name collision problem, which is why C programmers routinely use the enum identifier as prefix for the member names - essentially a homemade way of scoping them. C++ solved the problem by adding scoped enums.C++ scoped enums are more like Swift in that they don't implicitly convert to the base type. That's not what I'm looking for. What I'm looking for is deferring the lookup of enum member names until we know the type.No ```d bar(#a); // cannot match int a ```This doesn't scale well. ```d enum A { a, b, c } alias a = A.a; alias b = A.b; alias c = A.c; void bar(A a) {} void foo() { int a = 5; bar(a); // error; }Doesn't your proposal have the same error?No, they aren't. Because enum #values can only match enums. I wrote this little piece of code to demonstrate a proof of concept feature (based on existing D syntax). It only works for equality, but probably could be extended for other operations. It will not work for the full features I am looking for (i.e. assignment and implicit conversion for function parameters). But it gives you an idea how you can defer the type checking until it's needed. ```d struct EnumValComparer { struct EVC(string s) { bool opEquals(T)(T val) if (is(T == enum)) { static assert(is(typeof(__traits(getMember, T, s)))); return __traits(getMember, T, s) == val; } } property auto opDispatch(string s)() { return EVC!s(); } } EnumValComparer ev; enum ReallyReallyLongName { one, two, three } void main() { auto x = ReallyReallyLongName.one; assert(x == ev.one); } ``` imagine instead of `ev.one` you wrote `#one`. You can imagine that the compiler can figure this stuff out for everything, including arbitrary expressions between enum values, because it already is dealing with AST, and not simply strings. -Steve--- mod2.d: import mod1; int a = 5; void foo() { bar(.a); // error; } ``` And I also have used `with` statements, which *mostly* works, but I've come across some head-scratching problems when naming conflicts occur.Naming conflicts are worse with the proposed rules.
Jul 01 2022
On 7/1/2022 3:48 PM, Steven Schveighoffer wrote:These are not unscoped enums. There is a fundamental disconnect with what I wrote and what you are thinking I wrote. Swift's enums are not actually as loose type-wise as D's. They cannot convert to anything implicitly.I agree that disallowing implicit conversions is an important part of your proposal.Of course, that precludes `typeof(a)`, `a.sizeof`, `a + 3`, function templates, variadic function parameters. How do you distinguish an instance of an elem with the same name as an element of the enum?No, you don't. You only need to resolve the names when you are checking whether a function call matches, or an assignment/operation matches.Am I misunderstanding something?For one thing, you'll have to add all the members of all the enums to the symbol lookup.It's a combinatorial explosion *only if* you wrote function overloads for all the possible combinations.This implies deferring resolving expressions until trying to do matching, and constantly restarting that for each overload.C++ scoped enums are more like Swift in that they don't implicitly convert to the base type.I didn't know that.I understand what you're doing, but confess that makes me uncomfortable.Noenum A { a, b, c } alias a = A.a; alias b = A.b; alias c = A.c; void bar(A a) {} void foo() { int a = 5; bar(a); // error; }Doesn't your proposal have the same error?I wrote this little piece of code to demonstrate a proof of concept feature (based on existing D syntax). It only works for equality, but probably could be extended for other operations. It will not work for the full features I am looking for (i.e. assignment and implicit conversion for function parameters). But it gives you an idea how you can defer the type checking until it's needed.It's clever. With that, is a language change justified?
Jul 04 2022
On Monday, 4 July 2022 at 22:34:01 UTC, Walter Bright wrote:How do you distinguish an instance of an elem with the same name as an element of the enum?identifier.
Jul 04 2022
On 7/4/22 6:34 PM, Walter Bright wrote:On 7/1/2022 3:48 PM, Steven Schveighoffer wrote:Yes. The point is to avoid having to write `BigLongEnumTypeName`, and that's it. If you use it in places where it's not obvious it should be `BigLongEnumTypeName`, it fails. This does mean that in some cases, you just can't use this feature, and that would just have to be the way it is. One drawback of swift pulling out all the stops to infer everything is sometimes it can't, and that leads to annoying messages like "took too long to figure out, please split up your expression". I don't want that outcome at all for D.No, you don't. You only need to resolve the names when you are checking whether a function call matches, or an assignment/operation matches.Of course, that precludes `typeof(a)`, `a.sizeof`, `a + 3`, function templates, variadic function parameters.How do you distinguish an instance of an elem with the same name as an element of the enum?The literal syntax distinguishes it from everything else. My strawman was `#value`, but really, any not-already-used syntax will do. `.value` enum member". It can't be confused with anything else, just like you can't have symbols named `1`. easy-to-type symbol that doesn't already have an existing meaning. Another one that might work is `$`, because I think it would be unambiguous in this context, but also not sure.I don't feel like this is that difficult, but I profess ignorance on how it currently works. I imagine you could have an `EnumValueExpression` AST node, which contains the list of members that must be valid. Then when trying to match, you check the list of members against the enum itself.It's a combinatorial explosion *only if* you wrote function overloads for all the possible combinations.This implies deferring resolving expressions until trying to do matching, and constantly restarting that for each overload.Understandable. It's not something I really have enough knowledge to make you feel more comfortable with it. This is like a guy who "knows a little bit about engines" explaining to a mechanic what he wants done to his car. I hope someone with more compiler experience can jump in and give some more solid evidence that it's sound. Or alternatively explain why it's a really bad idea.I understand what you're doing, but confess that makes me uncomfortable.Noenum A { a, b, c } alias a = A.a; alias b = A.b; alias c = A.c; void bar(A a) {} void foo() { int a = 5; bar(a); // error; }Doesn't your proposal have the same error?Yes, because I can't hook assignment, construction, function parameter conversion, etc. I.e. I can't hook implicit conversion to a target type. I can hook any kind of binary operation, because `opBinaryRight` exists, but that would leave something that is half clever but fails in a lot of cases. Enough to make it more frustrating than is worth it. However, it does show the same methodology that I am thinking of -- defer the type resolution until it's used, just save the name(s). -SteveI wrote this little piece of code to demonstrate a proof of concept feature (based on existing D syntax).It's clever. With that, is a language change justified?
Jul 04 2022
On Tuesday, 5 July 2022 at 01:15:28 UTC, Steven Schveighoffer wrote:The literal syntax distinguishes it from everything else. My strawman was `#value`, but really, any not-already-used syntax will do. `.value` is not available, neither is `value`. -StevePerhaps : could work as `:value` isn't currently a reserved syntax today, unless something comes before, so it should definitely work in this case.
Jul 04 2022
On 7/5/22 2:34 AM, bauss wrote:On Tuesday, 5 July 2022 at 01:15:28 UTC, Steven Schveighoffer wrote:I thought that too. But there is existing syntax that might interfere: `cond ? value1 :value2`. I don't know enough to be sure, so I just picked one that isn't used. -SteveThe literal syntax distinguishes it from everything else. My strawman was `#value`, but really, any not-already-used syntax will do. `.value` is not available, neither is `value`. -StevePerhaps : could work as `:value` isn't currently a reserved syntax today, unless something comes before, so it should definitely work in this case.
Jul 05 2022
On Tuesday, 5 July 2022 at 15:06:01 UTC, Steven Schveighoffer wrote:I thought that too. But there is existing syntax that might interfere: `cond ? value1 :value2`. I don't know enough to be sure, so I just picked one that isn't used. -SteveI don't think that would interfere, because `cond ? value1 value2` isn't allowed, so if value2 is an enum value then it would be `cond ? value1 : :value2`
Jul 06 2022
On 7/6/22 3:05 AM, bauss wrote:On Tuesday, 5 July 2022 at 15:06:01 UTC, Steven Schveighoffer wrote:Probably fine, I agree. Again, just being cautious. I will stress also that my point isn't to propose a specific syntax, but a specific idea. Note that even if there is not an ambiguity in the grammar, there is still the confusion of the person reading such code. -SteveI thought that too. But there is existing syntax that might interfere: `cond ? value1 :value2`. I don't know enough to be sure, so I just picked one that isn't used.I don't think that would interfere, because `cond ? value1 value2` isn't allowed, so if value2 is an enum value then it would be `cond ? value1 : :value2`
Jul 06 2022
On 7/1/22 21:37, Steven Schveighoffer wrote:On 7/1/22 2:32 PM, Walter Bright wrote:I have implemented D's pretty simple function overloading rules in my experimental frontend. If you get a name clash you might get an ambiguity error, and other details are a consequence of the existing overloading rules, e.g.: ```d import std; alias Poly(T)=T delegate(T=T.init); enum poly(string name)=q{ (x){ static if(is(typeof(typeof(x).%s))) return typeof(x).%s; } }.format(name,name); enum Foo{ a, b, } enum Bar{ b, c, } void fun(Poly!Foo foo, int x){ writeln(foo()); } void fun(Poly!Bar bar, float x){ writeln(bar()); } void main(){ fun(mixin(poly!"b"), 1); // what does this do? } ``` (Answer: It will call the first overload, because the lambda is considered a perfect match after type deduction.) So I guess actually there is one open question: At what matching level should a polysemous enum literal match an enum type? If it's considered a perfect match, other parameters might cause a resolution of a name clash, if it's considered a match with implicit conversion, this does not happen. Some may therefore prefer to consider it a match with implicit conversion so the compiler is more conservative and happier to deal out ambiguity errors. As different enum types are incompatible, you will never get a resolution because an overload is more specialized unless all matching overloads are with the same enum type. In any case, everything is easily explained with the existing overloading rules plus one new definition of a matching level for polysemous enum literals. The pitfalls remain basically the same.On 7/1/2022 8:42 AM, Steven Schveighoffer wrote:This is why I'm asking the questions. I don't have experience on how to implement function overloading, and I don't know what pitfalls might be introduced from doing something like this.How I would design it:Those are reasonable suggestions. But just let me throw this out. I've heard from many sources that nobody understands C++ function overloading. It is described with pages of detail in the Standard. Even compiler writers only *temporarily* understand it while they are implementing it. How C++ programmers deal with it is they randomly try things until it works.
Jul 05 2022
On 7/5/22 7:53 PM, Timon Gehr wrote:So I guess actually there is one open question: At what matching level should a polysemous enum literal match an enum type? If it's considered a perfect match, other parameters might cause a resolution of a name clash, if it's considered a match with implicit conversion, this does not happen.I'm concerned about that though, because this brings what should be an exact match (if you specified the enum fully) into the same category as an implicit conversion. For example: ```d enum E { one } foo(E e, uint u); // 1 foo(E e, int i); // 2 foo(E.one, 1U); // calls 1 foo(E.one, 1); // calls 2 // Now both foo's are at the same level, and there's an ambiguity error // because uint <-> int foo(#one, 1U); ``` I'd much rather just consider that the type is deferred until the expression is used, and then resolved to the correct type based on the way it's used. The sole focus of this feature should be to just omit enum names when they are *obvious*. -Steve
Jul 05 2022
On 7/6/22 02:57, Steven Schveighoffer wrote:On 7/5/22 7:53 PM, Timon Gehr wrote:Then you get the same behavior as with lambdas. ```d enum Foo{ a, b } enum Bar{ b, c } void fun(Foo foo, int x){ writeln(foo); } void fun(Bar bar, float x){ writeln(bar); } void main(){ fun(:b, 1); } ``` So I guess you are saying this should call the first overload? The reason I am bringing this up is that here, those calls are both legal: ```d fun(Foo.b, 1); // calls first overload fun(Bar.b, 1); // calls second overload ``` I don't think this is a huge problem, but it might qualify as a pitfall. (It could be argued that removing the enum name from working code should either preserve the behavior or error out.) The overloading rules are not getting any more complicated though.So I guess actually there is one open question: At what matching level should a polysemous enum literal match an enum type? If it's considered a perfect match, other parameters might cause a resolution of a name clash, if it's considered a match with implicit conversion, this does not happen.I'm concerned about that though, because this brings what should be an exact match (if you specified the enum fully) into the same category as an implicit conversion. For example: ```d enum E { one } foo(E e, uint u); // 1 foo(E e, int i); // 2 foo(E.one, 1U); // calls 1 foo(E.one, 1); // calls 2 // Now both foo's are at the same level, and there's an ambiguity error // because uint <-> int foo(#one, 1U); ``` I'd much rather just consider that the type is deferred until the expression is used, and then resolved to the correct type based on the way it's used. The sole focus of this feature should be to just omit enum names when they are *obvious*. -Steve
Jul 05 2022
On 7/5/22 9:12 PM, Timon Gehr wrote:Then you get the same behavior as with lambdas. ```d enum Foo{ a, b } enum Bar{ b, c } void fun(Foo foo, int x){ writeln(foo); } void fun(Bar bar, float x){ writeln(bar); } void main(){ fun(:b, 1); } ``` So I guess you are saying this should call the first overload?Yeah, I think this is better, it's what I would expect. But I can see your point. It's also fine your way as well. The fact that there's very few types that convert to each other limits this case to very few overload sets.The reason I am bringing this up is that here, those calls are both legal: ```d fun(Foo.b, 1); // calls first overload fun(Bar.b, 1); // calls second overload ``` I don't think this is a huge problem, but it might qualify as a pitfall. (It could be argued that removing the enum name from working code should either preserve the behavior or error out.) The overloading rules are not getting any more complicated though.Either way is arguable I think. There is something to be said for erring on the side of caution (or ambiguity in this case). -Steve
Jul 05 2022
On Friday, 1 July 2022 at 15:42:20 UTC, Steven Schveighoffer wrote:(Snip)This is exactly how it should work. I think this is a pretty good feature. You want the strong typing, without namespace pollution, and without having to type everything out. It looks like the other C replacement languages see the value in it. Ada of all languages actually works very similarly: ```ada function foo is type thing is (a, b, c); type thang is (b, c, a); x : thing := a; y : thang := a; -- no ambiguity begin -- ... end foo; ``` We should learn a thing or two from Ada type features! Like using enums and ranges of enums as array bounds, for example. Ada is not a smooth language, but they somehow nailed stuff like this.
Jul 03 2022
On 6/30/2022 2:19 AM, bauss wrote:I don't think D could ever settle on a single rule here that a majority would agree upon.C++ threw in the towel and added scoped enums with a somewhat different syntax. Now C++ has two different kinds of enums. Having too many choices is not good language design.
Jun 30 2022
On Friday, 1 July 2022 at 04:53:01 UTC, Walter Bright wrote:On 6/30/2022 2:19 AM, bauss wrote:C and D don't have an enumeration type. `enum class` gave C++ a somewhat type safe enumeration type. It is very useful.I don't think D could ever settle on a single rule here that a majority would agree upon.C++ threw in the towel and added scoped enums with a somewhat different syntax. Now C++ has two different kinds of enums. Having too many choices is not good language design.
Jul 01 2022
On Friday, 1 July 2022 at 09:02:12 UTC, Ola Fosheim Grøstad wrote:C and D don't have an enumeration type. `enum class` gave C++ a somewhat type safe enumeration type. It is very useful.Absolutely. This is why we can write this insanity: ```d enum E {absolutely} const a = E.absolutely.absolutely.absolutely.absolutely.absolutely; ```
Jul 01 2022
On 7/1/2022 3:49 AM, user1234 wrote:enum E {absolutely} const a = E.absolutely.absolutely.absolutely.absolutely.absolutely;Indubitably.
Jul 01 2022
On Friday, 1 July 2022 at 10:49:46 UTC, user1234 wrote:On Friday, 1 July 2022 at 09:02:12 UTC, Ola Fosheim Grøstad wrote:Isn't that just because a value also has the properties of its type? So even if D was strict about only using declared values for an enum type instance, your example would still work.C and D don't have an enumeration type. `enum class` gave C++ a somewhat type safe enumeration type. It is very useful.Absolutely. This is why we can write this insanity: ```d enum E {absolutely} const a = E.absolutely.absolutely.absolutely.absolutely.absolutely; ```
Jul 02 2022
Just right now i got annoyed with enums again while porting an old library ![screenshot](https://i.imgur.com/opXDWh5.png) ```D ctx.allocator.create!(JSONValue)(JSONValue.ValueType.NULL) ``` vs ```D ctx.allocator.create!(JSONValue)(.NULL) ctx.allocator.create!(JSONValue)(#NULL) ctx.allocator.create!(JSONValue)(_.NULL) ``` Unnecessary repetitions and verbosity in my opinion I could put the with() next to the switch, but still, it's moving the problem
Jul 02 2022
On 6/30/2022 2:08 AM, Ogi wrote:I was under impression that this is a deliberate (although questionable) design to avoid some bugs rather some technical limitation.It is deliberate, not a bug. The rationale is the common C practice: enum XX { XXabc, XXdef, XXghi }; where the programmer is using a workaround for a scoped name.
Jun 30 2022
On Friday, 1 July 2022 at 04:50:39 UTC, Walter Bright wrote:On 6/30/2022 2:08 AM, Ogi wrote:The proposal is not to mimic C, is to make use of the type system so we get to remove the unnecessary verbosity If `.` is not the way to go, what about: `set_flags(_.FLAG_A | _.FLAG_B)` or `set_flags(with(FLAG_A | FLAG_B))` or `set_flags(auto(FLAG_A | FLAG_B))`I was under impression that this is a deliberate (although questionable) design to avoid some bugs rather some technical limitation.It is deliberate, not a bug. The rationale is the common C practice: enum XX { XXabc, XXdef, XXghi }; where the programmer is using a workaround for a scoped name.
Jul 01 2022