Discussion Thread: DIP 1044--Enum Type Inference--Community Review Round 1
Quirin Schroll
qs.il.paperinik at gmail.com
Wed Nov 23 09:38:06 UTC 2022
On Wednesday, 23 November 2022 at 03:24:30 UTC, Steven
Schveighoffer wrote:
> [snip]
>
> But if an implicit `with(Role)` is added to the case clause,
> now it only ever refers to `Role.member`. Essentially, you will
> end up with a switch that looks like:
>
> ```d
> switch(e) {
> case Role.member: ...
> case Role.member: ...
> case Role.member: ...
> default: ...
> }
> ```
>
> Which will then be a compiler error.
>
> The fix would be to rename the iteration variable to something
> the user couldn't possibly use. It's not pretty.
TL;DR: Use a version of `with` that matches lazily, i.e. when the
identifier resolved to nothing else, and not greedily as the
current one. [End of TL;DR]
Best thing is, `lazy` is already an aptly named keyword and can
be combined with `with` to form `lazy with` (same as `static` and
`if` or `foreach` or `assert` combine to effectively two-word
keywords).
The problem is that `with(expr)` is greedy: If it can resolve an
identifier `ident` as `expr.ident`, it will take it and not give
it up even if that leads to a fail. (Like Regex greedy matching,
thus the name.) The implicit `with` must be lazy to be useful: If
`ident` cannot be resolved otherwise, in the context of `lazy
with(arg)` an attempt for `arg.ident` is made.
In the above code, even if `Role` has a member named `member`,
the function local `member` is considered:
```d
import std.stdio;
enum Role
{
guest,
member,
developer,
}
void main()
{
Role r;
lazy with (typeof(r)) // implicitly added?
switch(r)
{
static foreach(member; EnumMembers!Role )
{
case member: // sees foreach variable, therefore lazy
with does nothing.
...
}
}
lazy with (typeof(r)) // implicitly added?
switch(r)
{
case guest: // as no other `guest` in scope, lazy with makes
it Role.guest
...
case member: // as no other `member` in scope, lazy with
makes it Role.member
...
}
}
```
If I’m not missing something, the only sad case is this:
```d
immutable member = Role.developer; // Who does this anyways?
lazy with (typeof(r))
switch(r)
{
case guest: // lazy with makes it Role.guest
...
case member: // `member` in scope, effectively Role.developer.
...
}
```
The same can be done for declarations without `auto` and maybe
assignments:
```d
Role r = member;
r = guest;
```
is as if:
```d
Role r = () { lazy with(Role) return member; }();
lvalueExpresson = () { lazy with(typeof(lvalueExpresson)) return
guest; }(); // maybe?
```
I guess declarations are unproblematic. For assignments, it might
be surprising because lvalue expressions can be complex and a
reminder for the type might be appropriate. On the other hand, if
that’s the case, nothing stops you from being explicit. Another
issue with assignment expressions is that not all left-hand side
expressions have a type: Property setters and opIndexAssign don’t
trivially give you the supposed right-hand side type. The type
might even be a template type parameter. It’s a best effort
solution at best.
More information about the Digitalmars-d
mailing list