Open InsertCreativityHere opened 2 years ago
I think C# behavior is quite good, the locally defined type hides imported types, there is only a conflict if you use a type not defined locally that is defined in more that one of the imported namespaces.
Unlike a using_alias_directive, a using_namespace_directive may import types whose identifiers are already defined within the enclosing compilation unit or namespace body. In effect, names imported by a using_namespace_directive are hidden by similarly named members in the enclosing compilation unit or namespace body.
Think of something like
// Foo.slice
module Foo
{
interface Bar{}
interface Baz{}
}
// Foe.slice
using Foo;
module Foe
{
interface Bar // Doesn't make sense for this to be an error
{
Baz getBaz(); // Baz refers to Foo::Baz
}
}
This seems also consistent with what we do for nested modules
module Foo
{
interface Bar {}
module Nested
{
interface Bar {} // Hides Foo::Bar
}
}
This seems also consistent with what we do for nested modules
Yes, you're right. I didn't think about that.
So, type resolution would look like:
1) Check the current module for the type
2) Recursively check enclosing modules for the type, working outwards (from current module to global scope)
3) Check all the modules you're using
for the type.
If only 1 module has the type, use it, if multiple modules have it, report an ambiguity error
Importantly, we wouldn't check the enclosing modules for using directives. Either the type is directly in that module or it isn't.
I prefer that we error on any ambiguity and make the user qualify it.
// A.slice
module A
{
interface Foo {}
}
/// B.slice
using A;
module B
{
interface Foo // not ambiguous
{
getAFoo() -> A::Foo; // not ambiguous
getBFoo() -> B::Foo; // not ambiguous
getFoo() -> Foo; // ambiguous (fails to compile)
}
module C
{
interface Foo {} // not ambiguous
struct Data
{
f1: C::Foo // not ambiguous
f2: A::Foo // not ambiguous
f3 B::Foo // not ambiguous
f: Foo // ambiguous
}
}
}
I find it odd that you will have to use the scope of the current module, and it is not consistent with nested module rules
module Foo
{
interafece A
{
getA() -> A // not ambiguous Foo::A
}
module Bar
{
interafece A
{
getA() -> A // not ambiguous Foo::Bar::A
}
}
}
Also consider you can be adding using A
because you are trying to bring a few types in the current context, but why bother having to qualify all the types event the ones you are not trying to use. Also another case is
// A.slice
module A
{
interface Bar{}
}
/// B.slice
using A;
module B
{
interface Foo // not ambiguous
{
getBar() -> Bar; // not ambiguous
getFoo() -> Foo; // not ambiguous
}
}
Now you go and add interface Foo
to A.slice
and this breaks compilation of B.slice
even when B.slice
doesn't care about this new type?
I find it odd that you will have to use the scope of the current module, and it is not consistent with nested module rules
I updated my example, that's also ambiguous.
Now you go and add interface Foo to A.slice and this breaks compilation of B.slice even when B.slice doesn't care about this new type?
Isn't that just a problem in general with "including" things from different files.
Many languages provide a way of 'pulling in' other namespaces:
using MyNamespace
using MyNamespace
import MyPackage
use MyModule
Slice should also provide a feature like this, to avoid needing to repeat long namespaces.
Proposed Syntax
It MUST be declared at the top of a slice file, before any module declarations (Same as file attributes). Additionally, it's file-specific and NOT exported. Only the contents of the slice file will be affected by this.
Open Questions What happens when there's a conflict? If a locally declared type conflicts with a type that is being pulled in. A) We silently hide the pulled-in type, and the locally declared type is what gets used B) We issue an error saying that the type is ambiguous. I lean heavily towards B, otherwise it seems very unsafe to me. But apparently C# went with option A, so idk.