Comment #0 by qs.il.paperinik — 2022-11-23T10:03:34Z
I suggest an alternative version of `with (typeOrExpr)` that resolves an identifier `id` to `typeOrExpr.id` only if `id` cannot be resolved otherwise.
Its syntax can be `lazy with (typeOrExpr)`.
Example:
```d
enum A { x, y }
enum B { y, z }
void main()
{
with (A)
with (B)
{
pragma(msg, typeof(x)); // prints: A
pragma(msg, typeof(y)); // prints: B //!
pragma(msg, typeof(z)); // prints: B
}
lazy with (A)
lazy with (B)
{
pragma(msg, typeof(x)); // prints: A
pragma(msg, typeof(y)); // prints: A //!
pragma(msg, typeof(z)); // prints: B
}
}
```
When resolving `y`, in the first case, `with (B)` goes ahead and resolves it (greedily) and `with (A)` has nothing left to do.
In the second case, `lazy with (B)` asks its surrounding context to resolve `y` and `lazy with (A)` then does the same. The answer for `lazy with (A)` is that no `y` is in scope, thus attempt to resolve `y` as `A.y` is made and succeeds. Finally, `lazy with (B)` has no identifiers to resolve.
Comment #1 by qs.il.paperinik — 2022-11-24T09:40:47Z
Regular (greedy) `with` is unusable in generic code when the `with`ed type or expression is unknown. Greedy `with` can shadow any local identifier, which ā Iād boldly claim ā is *never* intended.
`lazy with` on the other hand will never make an identifier visible in your scope mean something else. Use-cases in generic code are still rare, but when the `with`ed type or expression is required/expected to provide *some* specific members, it can be used.
Comment #2 by robert.schadek — 2024-12-13T19:25:50Z