DIPX: Enum Literals / Implicit Selector Expression

Steven Schveighoffer schveiguy at gmail.com
Fri Jul 1 22:48:57 UTC 2022


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


More information about the Digitalmars-d mailing list