May I introduce `lazy with`?

Quirin Schroll qs.il.paperinik at gmail.com
Fri Nov 25 08:58:53 UTC 2022


On Thursday, 24 November 2022 at 19:27:00 UTC, Paul Backus wrote:
> On Thursday, 24 November 2022 at 17:41:20 UTC, Quirin Schroll 
> wrote:
>> The essence of the problem is that `with (TypeOrExpr)` will 
>> prefer resolving an identifier `id` as `TypeOrExpr.id` 
>> whenever `TypeOrExpr.id` is viable:
>> ```d
>> with (EnumType)
>> switch (enumValue)
>> {
>>     static foreach (member; EnumMembers!EnumType)
>>     {
>>         case member: // What if EnumType has a member named 
>> `member`?
>>             ...;
>>     }
>> }
>> ```
>> When `EnumType` happens to have a member named `member`, 
>> `with` will greedily take it and the loop will produce 
>> nonsense.
>
> I tried it on run.dlang.io and `member` is correctly resolved 
> as referring to the loop variable, not the enum member. Here's 
> a complete, compilable example:

Sorry, I gust believed that Steven’s post was based on testing 
himself, especially note the sentence I highlighted.

On Wednesday, 23 November 2022 at 03:24:30 UTC, Steven 
Schveighoffer wrote:
> To spell it out, when you write `case member:` what does 
> `member` refer to? It currently, in the implementation of 
> Phobos refers to the `member` iteration variable of the foreach 
> inside to!string:
>
> ```d
> foreach(member; EnumMembers!E) {
> ```
>
> **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: ...
> }
> ```

Steven’s claims are based on what the spec [Statement § With 
Statement](https://dlang.org/spec/statement.html#with-statement) 
says what should happen:
>  Within the with body the referenced object is searched first 
> for identifier symbols.

And a little later:
> Use of `with` object symbols that shadow local symbols with the 
> same identifier are not allowed.

So, Steven seemingly didn’t test and read the spec section 
completely and the compiler has a bug: The `static foreach` is 
the greedy one here and `with` never gets to see `member` as a 
plain identifier.

I guess we could reasonably give `static foreach` that high 
priority officially. Still, the point stands that `with` is 
greedy enough that it ambiguates local symbols and shadows 
lower-scope symbols. `lazy with` would not do that.


More information about the Digitalmars-d mailing list