Also, "io", "file", "random" etc are commonly used variables, so the issue with shadowing is real.
`File file = file::open(...)` is completely unambiguous and fine. `File file = file.open(...)` on the other hand would be bad.
If the language had flat modules, or no path shortening, then it would be possible.
D uses a spell checker for undefined identifiers, and the dictionary is all the identifiers in scope. It has about a 50% success rate in guessing which identifier was meant, which is quite good.
> Also, "io", "file", "random" etc are commonly used variables, so the issue with shadowing is real.
If the same identifier is accessible through multiple lookup paths, an error is issued. If a local variable shadows a variable in an outer scope, and error is issued.
We've developed this over several years, and it works quite well.
Path shortening can be done with:
alias open = file.open;
or: import io: open;In Rust where modules share a namespace with other identifiers, I just pick different variable names, or write my imports so they don't conflict. It's not that big a deal.
The identifier on the right is looked up in the scope of the identifier on the left. If it resolves to a module, then it's a module. If it resolves to a function, then it's a function. If the left side is a pointer (not a symbol with a scope) then the right side resolves to a member.
It also makes refactoring much easier - changing a pointer to a reference does not require a global search/replace of -> with .