Jump to page: 1 2
Thread overview
DIPX: Enum Literals / Implicit Selector Expression
Dec 02, 2021
russhy
Dec 02, 2021
Dennis
Dec 02, 2021
Paul Backus
Dec 03, 2021
Dave P.
Dec 03, 2021
Elronnd
Dec 03, 2021
Dave P.
Dec 03, 2021
Elronnd
Dec 03, 2021
bauss
Dec 03, 2021
bauss
Dec 03, 2021
russhy
Dec 03, 2021
Nick Treleaven
Dec 03, 2021
user1234
Dec 03, 2021
Rumbu
Dec 03, 2021
Dave P.
Jun 25, 2022
ryuukk_
Jun 25, 2022
Basile B.
Jun 26, 2022
ryuukk_
Jun 26, 2022
ryuukk_
Jun 29, 2022
ryuukk_
December 02, 2021

DIPX: Enum Literals / Implicit Selector Expression

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

December 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/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:

// 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?
// 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[]?
// 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.

December 02, 2021

On Thursday, 2 December 2021 at 19:44:07 UTC, russhy wrote:

>

DIPX: Enum Literals / Implicit Selector Expression

[...]

>

Link: https://gist.github.com/RUSShyTwo/2fcf6d294d25ba748d61927b5eed2944

First impressions:

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:

auto tag = MyTag.A;

Meanwhile, this:

void what(MyTag tag)
{}
what(.B);

...is potentially troublesome, because it is grammatically ambiguous with the module scope operator. For example:

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?
December 03, 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:

>

DIPX: Enum Literals / Implicit Selector Expression

[...]

>

[...]

First impressions:

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:

auto tag = MyTag.A;

Where this really shines are with bit flags. Enabling this would be quite nice:

MyFlags flags = .A | .B | .C;
functionWithFlags(.Foo | .Bar, .Baz | .Qux);

vs.

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 .

December 03, 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.
December 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:

>

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:

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.

December 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/2fcf6d294d25ba748d61927b5eed2944

In C# you can bring all enum members into scope with using static

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

static alias MyEnum;

Currently there is a proposal champion for the dot notation also in C#, maybe we can learn something: https://github.com/dotnet/csharplang/issues/2926

December 03, 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 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.
December 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:

>

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.

December 03, 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:

>

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:

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:

    Flags flags;
    with(Flags){
        flags = FOO | BAR | BAZ | QUX;
    }
    // flags does exist here now.
« First   ‹ Prev
1 2