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