import concerns (was Re: Historical language survey)

Walter Bright newshound at digitalmars.com
Fri Jul 7 17:53:19 PDT 2006


kris wrote:
> to clarify, here's some examples:
> 
> --------------
> module foo;
> 
> import bar;
> import wumpus;
> 
> extern (C) int printf (char*, ...);
> 
> class Bar {char[] toString() {return "foo.Bar";}}
> 
> void main()
> {
>         auto bar = new Bar;
>         auto wumpus = new Wumpus;
> 
>         printf ("%.*s\n", bar.toString);
>         printf ("%.*s\n", wumpus.toString);
> }
> -------------
> 
> 
> -------------
> module bar;
> 
> class Bar {char[] toString() {return "bar.Bar";}}
> -------------
> 
> 
> -------------
> module wumpus;
> 
> class Wumpus {char[] toString() {return "wumpus.Wumpus";}}
> -------------
> 
> 
> What's interesting here is the lack of conflict between bar.Bar and 
> foo.Bar. The compiler ignores the conflicting names and uses foo.Bar 
> within main().
> 
> Now, assume both modules foo and bar are from different vendors. Module 
> bar gets changed at some point to this:
> 
> -------------
> module bar;
> 
> class Bar {char[] toString() {return "bar.Bar";}}
> 
> class Wumpus {char[] toString() {return "bar.Wumpus";}}
> -------------
> 
> 
> The vendor added a Wumpus class to the module. Quite innocent. In this 
> case, the program now fails to compile, and the user-code needs to be 
> re-engineered. The amount of redundant work may be small, or it may be 
> very large. This is simply redundant work ~ it should not be necessary 
> at all.
> 
> One way to avoid the re-engineering is to use
> "import wumpus as ...."
> "import bar as ...."
> 
> In this case, the instances of Bar and Wumpus must be fully qualified -- 
> you can't access them any other way. Thus it would be "auto bar = new 
> bar.Bar;", or whatever.
> 
> Another approach is to import explicitly, just like Modula-3 does:
> 
> import Bar from bar;
> import Wumpus from wumpus;
> 
> In this case, it's pretty clear than any additions to modules from 
> either vendor will not result in re-engineering work. It's also a bit 
> closer to the current D model.
> 
> 
> Another question is this: why is there no conflict between the two Bar 
> declarations in the first case, while there is between the two Wumpus 
> instances in the second case? I suspect this is down to where the decl 
> actually resides (which module).
> 
> 
> 
> 
> 
> 
> kris wrote:
>> Walter Bright wrote:
>>
>>> kris wrote:
>>>
>>>> D imports an entire module, into the current namespace (or some 
>>>> variation upon that). This means that any additions to the original 
>>>> module have to be aware of the namespace usage of *any* module that 
>>>> imports the original. Otherwise, a namespace collision will occur 
>>>> and the combination will fail to compile. M3 import explicitly from 
>>>> each module instead ~ you can't have such a collision. The value of 
>>>> that is just as solid today as it was in 1989.
>>>>
>>>> One might argue that with D, one should create new modules instead 
>>>> of extending existing ones? That's a fair point until you consider 
>>>> that the module namespace is limited to one file, and the 'friend' 
>>>> aspect is limited to one module (private attributes being visible 
>>>> within the one module). Thus, D suffers this problem in a notable 
>>>> manner.
>>>>
>>>> I forget whether M3 supports importing into a distinct namespace or 
>>>> not --- the "import x.y.z. as foo;" syntax -- but that can alleviate 
>>>> related problems, and would help resolve the current D namespace 
>>>> conflicts that are quite prevalant?
>>>
>>>
>>>
>>> import namespaces are second class citizens in D - they are easily 
>>> overridden by using aliases or fully qualified lookups:
>>>
>>> import a;    // defines foo()
>>> import b;    // defines foo()
>>>
>>> foo();        // ambiguous
>>> a.foo();    // doesn't matter if there's a b.foo
>>> b.foo();    // works
>>>
>>> alias a.foo foo;
>>> foo();        // works
>>>
>>> As for import x.y.z. as foo;, you can do:
>>>
>>> alias x.y.z foo;
>>> foo.bar();
>>>
>>> alias x.y abc;
>>> abc.x.bar();
>>>
>>> alias x def;
>>> def.y.z.bar();
>>>
>>> The alias works at any level you choose to make it. Alias can be used 
>>> to 'import' any name into the current namespace, making it first class.
>>>
>>> The second class lookup capability is to make it easier to write 
>>> quick and dirty programs. Aliases or fully qualified names should be 
>>> used when writing large, complex apps. Think of it like using private 
>>> - you wouldn't bother with it for small or throwaway programs, but 
>>> you wouldn't think of not using it for long lived or complex apps.
>>
>>
>>
>> Yes, I'm aware of those various workarounds, but none of them address 
>> the issue. As I'm sure you're aware of, all of these need to be used 
>> at the import site ... not in the importee code. This is where the 
>> issues arise.
>>
>> What I was getting at is this:
>>
>> -------------
>> module importee;
>>
>> class Foo {}
>> -------------
>>
>> and
>>
>> -------------
>> module importer;
>>
>> import importee;
>>
>> class Bar {}
>>
>> class Bazooka {}
>> -------------
>>
>>
>> now, suppose we later change module importee like so:
>>
>> -------------
>> module importee;
>>
>> class Foo {}
>>
>> class Bazooka {}
>> -------------
>>
>> Now, module importer will not compile.

Yes, it will, because names in the module being compiled take priority 
over imported names. A conflict only happens if two or more imports 
define the same name.

>> The second aspect is the whole alias notion is just too weak to handle 
>> large-scale development. In other languages, the syntax "import x.y.z 
>> as foo;" actually does create a unique namespace, achieving two things:
>>
>> a) there's no other way to refer to x.y.z content other than through 
>> the "foo." prefix. This eliminates the potential for conflicting names 
>> across multiple modules, regardless of long-term maintenance in any of 
>> them. Relying on the D "alias" mechanism for such needs is prone to 
>> abject failure.

Why is it prone to abject failure? The only thing really necessary is 
that the module names themselves be unique or be in a package with a 
unique name.

>> b) alias simply provides an /additional/ means of referring to some 
>> element. All of the original names are still there, from the entirety 
>> of the imported module. The potential for name collisions, such as two 
>> classes called 'Bazooka' is painfully obvious.

Not if you use either fully qualified names when importing, or you use 
an alias to pick which Bazooka you want.

>> The whole concept of long-term and/or large-scale development using D 
>> as a tool is marred by such problems -- it's not very hard to fix 
>> either -- perhaps as simple as the "import x.y.z as foo;" syntax, 
>> which is quite quite different from the concept of alias. I sincerely 
>> hope you'll agree on that distinction?

I'm not sure it's that different. But I also think (because of the 
example above) there's a misunderstanding about how import name lookups 
work.



More information about the Digitalmars-d mailing list