import concerns (was Re: Historical language survey)
kris
foo at bar.com
Fri Jul 7 18:42:53 PDT 2006
Walter;
I added compilable code at the top of the post here, in a genuine
attempt to show you where the current design has a weakness. Yet, you
ignored those clarifications entirely. Focusing instead on pre-clarified
observations, which could be considered somewhat ambiguous, doesn't help
much at all :(
The code provided illustrates the weakness quite well. I do hope you'll
run the example provided, and address that instead?
Please see below:
Walter Bright wrote:
> 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 wumpus 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.
Please see the examples at the top of this post, and those in Sean's
post also.
>
>>> 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 point of this particular thread is to avoid needless
modifications to code which imports other modules; where those /other/
modules change over time. It appears that you're thinking purely in
terms of "I own all the code, and I'll be responsible for all of the
namespace issues". I sincerely hope we don't have to spell out the
limitations with such a notion?
>
>>> 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.
That's exactly why I added the working code examples at the top of the
post -- so you could try it out with the compiler, and observe for
yourself what it does. It demonstrates that the current import handling
is really quite brittle from a large-scale and/or long-term development
aspect. Please read the clarifications toward the top of this post.
More information about the Digitalmars-d
mailing list