www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIPX: Enum Literals / Implicit Selector Expression

reply russhy <russhyy gmail.com> writes:


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
next sibling parent Dennis <dkorpel gmail.com> writes:
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/Jai
Don'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
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 2 December 2021 at 19:44:07 UTC, russhy wrote:

[...]
 Link: 
 https://gist.github.com/RUSShyTwo/2fcf6d294d25ba748d61927b5eed2944
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; ``` 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
parent reply Dave P. <dave287091 gmail.com> writes:
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:

[...]
 [...]
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; ```
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 `.`
Dec 02 2021
parent reply Elronnd <elronnd elronnd.net> writes:
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
next sibling parent reply Dave P. <dave287091 gmail.com> writes:
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:
 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.
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.
Dec 02 2021
next sibling parent Elronnd <elronnd elronnd.net> writes:
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 scope
with-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
prev sibling next sibling parent reply bauss <jj_1337 live.dk> writes:
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:
 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.
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.
You could do this however: ```d Flags flags; with(Flags){ flags = FOO | BAR | BAZ | QUX; } // flags does exist here now. ```
Dec 03 2021
parent reply bauss <jj_1337 live.dk> writes:
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:
 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:
 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.
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.
You could do this however: ```d Flags flags; with(Flags){ flags = FOO | BAR | BAZ | QUX; } // flags does exist here now. ```
Or even: ```d Flags flags; with(Flags) flags = FOO | BAR | BAZ | QUX; // flags does exist here now. ```
Dec 03 2021
parent russhy <russhyy gmail.com> writes:
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
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Friday, 3 December 2021 at 04:58:51 UTC, Dave P. wrote:
 Except that `with` introduces a new scope
There was a proposal (with an implementation) to allow `with` without braces: https://forum.dlang.org/post/bwudoaknbiodwilvldxl forum.dlang.org
 and 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
prev sibling parent user1234 <user1234 12.de> writes:
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:
 Where this really shines are with bit flags.
Bitflags are a case where 'with' _does_ work.
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.
Dec 03 2021
prev sibling next sibling parent reply Rumbu <rumbu rumbu.ro> writes:
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/2fcf6d294d25ba748d61927b5eed2944
static` ```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
parent reply Dave P. <dave287091 gmail.com> writes:
On Friday, 3 December 2021 at 05:41:20 UTC, Rumbu wrote:
 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/2fcf6d294d25ba748d61927b5eed2944
static` [...] An equivalent D idiom will be ```d static alias MyEnum; ``` [...]
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.
Dec 03 2021
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
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
parent reply Basile B. <b2.temp gmx.com> writes:
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 sweet
I'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
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
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:
 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 sweet
I'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
Thanks a lot for your detailed and helpful reply, will give it another try today! Compiler stuff is quite intimidating
Jun 26 2022
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
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
parent ryuukk_ <ryuukk.dev gmail.com> writes:
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
prev sibling parent reply Ogi <ogion.art gmail.com> writes:
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
next sibling parent reply bauss <jj_1337 live.dk> writes:
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
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
parent bauss <jj_1337 live.dk> writes:
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:
 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.
I think that again depends on what one expects to happen, as it only solves it if that's the perceived correct behavior.
Jun 30 2022
prev sibling next sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Thursday, 30 June 2022 at 09:19:15 UTC, bauss wrote:
 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.
the goal is not to copy c/c++, it's to be smarter, they are not globals
 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 enum
 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 explicit
 auto 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
parent reply Mike Parker <aldacron gmail.com> writes:
On Thursday, 30 June 2022 at 10:51:31 UTC, ryuukk_ wrote:

 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 explicit
 auto 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
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?
Jun 30 2022
next sibling parent reply bauss <jj_1337 live.dk> writes:
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:

 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 explicit
 auto 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
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?
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
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
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:
 On Thursday, 30 June 2022 at 10:51:31 UTC, ryuukk_ wrote:

 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 explicit
 auto 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
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?
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.
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
Jun 30 2022
parent reply The Zealot <zod zod.zod> writes:
On Thursday, 30 June 2022 at 15:42:05 UTC, ryuukk_ wrote:
 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:
 [...]
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.
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
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.
Jun 30 2022
parent ryuukk_ <ryuukk.dev gmail.com> writes:
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:
 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:
 [...]
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.
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
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.
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, c
Jun 30 2022
prev sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
 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
next sibling parent bauss <jj_1337 live.dk> writes:
On Thursday, 30 June 2022 at 11:32:42 UTC, ryuukk_ wrote:
 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
Meh... if you need to specify that many enum values then perhaps you just need a new field with all those values.
Jun 30 2022
prev sibling parent reply ShadoLight <ettienne.gilbert gmail.com> writes:
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
parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Thursday, 30 June 2022 at 17:15:46 UTC, ShadoLight wrote:
 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.
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
Jun 30 2022
next sibling parent Dom Disc <dominikus scherkl.de> writes:
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
prev sibling parent reply ShadoLight <ettienne.gilbert gmail.com> writes:
On Thursday, 30 June 2022 at 20:32:49 UTC, ryuukk_ wrote:
 On Thursday, 30 June 2022 at 17:15:46 UTC, ShadoLight wrote:
 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,
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.
 It is not nice when you just want to call a function with your 
 enum
Maybe, 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 value
That 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
parent ryuukk_ <ryuukk.dev gmail.com> writes:
 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
prev sibling next sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Thursday, 30 June 2022 at 09:19:15 UTC, bauss wrote:
 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.
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
Jun 30 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/30/2022 8:41 AM, ryuukk_ wrote:
      Color color = .orange; <--- it obviously pick orange, it already
expect an 
 enum
That 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
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/1/22 1:28 AM, Walter Bright wrote:
 On 6/30/2022 8:41 AM, ryuukk_ wrote:
      Color color = .orange; <--- it obviously pick orange, it already 
 expect an enum
That 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.
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. -Steve
Jul 01 2022
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/1/22 2:22 PM, Walter Bright wrote:
 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.
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. -Steve
Jul 01 2022
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 7/1/22 21:20, Steven Schveighoffer wrote:
 On 7/1/22 2:22 PM, Walter Bright wrote:
 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.
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. -Steve
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.)
Jul 05 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Wednesday, 6 July 2022 at 18:30:10 UTC, Walter Bright wrote:
 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 :-/
Side note: Does D really needs function overloading? You can achieve the same effect by using meta-programming. - Alex
Jul 07 2022
parent reply Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
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.

 - Alex
Just wondering, how do you achieve it using meta-programming?
Jul 07 2022
parent reply Nick Treleaven <nick geany.org> writes:
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:
 Side note: Does D really needs function overloading? You can 
 achieve the same effect by using meta-programming.

 - Alex
Just wondering, how do you achieve it using meta-programming?
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.
Jul 07 2022
next sibling parent Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
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
prev sibling parent reply 12345swordy <alexanderheistermann gmail.com> writes:
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:
 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.

 - Alex
Just wondering, how do you achieve it using meta-programming?
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.
This can be solved by having a top type named "Any" to indicate that it can be any type for virtual functions. -Alex
Jul 08 2022
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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.
This can be solved by having a top type named "Any" to indicate that it can be any type for virtual functions.
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_functions
Jul 08 2022
parent 12345swordy <alexanderheistermann gmail.com> writes:
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:
 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
 		...
 }
 ```

 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.
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_functions
"TypeInfo" isn't a Top type though, nor should it be treated as one.
Jul 08 2022
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 7/6/22 20:30, Walter Bright wrote:
 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 :-/
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.)
Jul 07 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/1/22 2:32 PM, Walter Bright wrote:
 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.
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.
 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
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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.
 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; }
Doesn't your proposal have the same 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
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/1/22 5:23 PM, Walter Bright wrote:
 On 7/1/2022 12:37 PM, Steven Schveighoffer wrote:
 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.
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.
 Am I misunderstanding something?
For one thing, you'll have to add all the members of all the enums to the symbol lookup.
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.
 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.
 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 ```d bar(#a); // cannot match int a ```
 
 
 --- 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.
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
Jul 01 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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.
 Am I misunderstanding something?
For one thing, you'll have to add all the members of all the enums to the symbol lookup.
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?
 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.
 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
I understand what you're doing, but confess that makes me uncomfortable.
 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
next sibling parent TheGag96 <thegag96 gmail.com> writes:
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
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/4/22 6:34 PM, Walter Bright wrote:
 On 7/1/2022 3:48 PM, Steven Schveighoffer wrote:
 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.
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.
 
 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.
 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.
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.
 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
I understand what you're doing, but confess that makes me uncomfortable.
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 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?
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). -Steve
Jul 04 2022
parent reply bauss <jj_1337 live.dk> writes:
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`.

 -Steve
Perhaps : 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
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/5/22 2:34 AM, bauss wrote:
 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`.

 -Steve
Perhaps : could work as `:value` isn't currently a reserved syntax today, unless something comes before, so it should definitely work in this case.
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. -Steve
Jul 05 2022
parent reply bauss <jj_1337 live.dk> writes:
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.

 -Steve
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
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/6/22 3:05 AM, bauss wrote:
 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.
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`
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. -Steve
Jul 06 2022
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 7/1/22 21:37, Steven Schveighoffer wrote:
 On 7/1/22 2:32 PM, Walter Bright wrote:
 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.
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.
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.
Jul 05 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 7/6/22 02:57, Steven Schveighoffer wrote:
 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
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.
Jul 05 2022
parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
prev sibling parent TheGag96 <thegag96 gmail.com> writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Friday, 1 July 2022 at 04:53:01 UTC, Walter Bright wrote:
 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.
C and D don't have an enumeration type. `enum class` gave C++ a somewhat type safe enumeration type. It is very useful.
Jul 01 2022
parent reply user1234 <user1234 12.de> writes:
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
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/1/2022 3:49 AM, user1234 wrote:
 enum E {absolutely}
 const a = E.absolutely.absolutely.absolutely.absolutely.absolutely;
Indubitably.
Jul 01 2022
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
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:
 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; ```
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.
Jul 02 2022
parent ryuukk_ <ryuukk.dev gmail.com> writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Friday, 1 July 2022 at 04:50:39 UTC, Walter Bright wrote:
 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.
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))`
Jul 01 2022