July 10, 2006
Regan Heath wrote:
> Sub-thread for ideas.

My preference for syntax and semantics would be:

// regular import w/ optional alias (*, ***)
import std.stdio, std.c.stdlib as stdlib, std.date;

// symbol specific import w/ optional alias (**, ***)
import from std.stdio writef, writefln as print, readf;

// combined (***)
import std.stdio, from std.thread wait as wait, pause, std.date as date;

* Importing an entire module as an alias would then require a FQN to access any of the members, for example given the above:
malloc(...); // error
std.c.stdlib.malloc(...); // error
stdlib.malloc(...); // Ok

** A specific symbol that was not aliased would require a FQN to access. For example from above:
std.stdio.writef(...);

*** In any case, 'private import' would function as it does now.

This is all heavily based on the ideas of others and a summation of my understanding of the relevant threads.

Thanks,

- Dave
July 10, 2006
2 comments:

Dave skrev:
> Regan Heath wrote:
>> Sub-thread for ideas.
> 
> My preference for syntax and semantics would be:
> 
> // regular import w/ optional alias (*)
> import std.stdio, std.c.stdlib as stdlib, std.date;
> 
> // symbol specific lookup w/ optional alias (**)
> import from std.stdio writef, writefln as print, readf;

How would the parser disambiguate the import of the module readf from the symbol readf in std.stdio?

> // combined
> import std.stdio, from std.thread wait as wait, pause, std.date as date;
> 
> * Importing an entire module as an alias would then require a FQN to access any of the members, for example given the above:
> malloc(...); // error
> std.c.stdlib.malloc(...); // error
> stdlib.malloc(...); // Ok
> 
> ** A specific symbol that was not aliased would require a FQN to access. For example from above:
> std.stdio.writef(...);

Just to clarify, does this mean that to import std.c.stdlib as FQN only, you would have to do:

import std.c.stdlib as std.c.stdlib; //(1)

Or, indirectly:

import from std.c.stdlib a_symbol_I_will_never_use;

?

1) When you import something as fqn-only, you must be ok with typing out the full name quite a lot, so once more will not make much of a difference I guess...

/Oskar
July 10, 2006
In article <e8u2s1$1li1$1@digitaldaemon.com>, Chris Nicholson-Sauls says...
>
>jcc7 wrote:
>> In article <optcf2eoqc23k2f5@nrage>, Regan Heath says...
>> 
>>>Sub-thread for ideas.
>> 
>> 
>> I'm not thrilled about the prospect of reusing static for yet another purpose, but I don't think that we need to add "from" or "as" to the keyword list either.
>> 
>> Instead of "import fooTooLong.reallyTooLong as fooShort;"
>> 
>> we could re-use an operator:
>> "import fooTooLong.reallyTooLong = fooShort;"
>> 
>> I like it, but it could just be me.
>
>I think its a pretty neat idea, actually.  Although, maybe it should be the colon instead?

I thought about colon, but people will probably want to do things like this and I think the colon meaning could get confusing:

With equal symbol...

public:
import fooTooLong.reallyTooLong = fooShort;
import fooTooLong.reallyTooLong2 = fooShort2;


With colon symbol...

public:
import fooTooLong.reallyTooLong : fooShort;
import fooTooLong.reallyTooLong2 : fooShort2;


Okay, now that I look at it, I think it'd work fine.

..
>> "import final fooTooLong.reallyTooLong;"
>> "import do fooTooLong.reallyTooLong;"
>> "import void fooTooLong.reallyTooLong;"
>
>Do? Do?? Do?!  ;)  Why not 'default'?  Or 'typedef'?  (Actually, that'd be cute.)

I know I'm grasping at straws. At this point, D has plenty of keywords, so I'm trying to encourage recycling. ;)


>I still think we need something like an 'fqn' keyword to accomplish this.  Or possibly the elsewhere-proposed 'fqnimport' statement.

We could do that, but "fqn" and "fqnimport" look very ugly to me. I'm sure we can find something more elegant than that that makes more sense than "switch import".

jcc7
July 10, 2006
In article <iq7sr366x54o$.11elk5mgt7qt0$.dlg@40tude.net>, Derek Parnell says...
>
>On Mon, 10 Jul 2006 11:04:39 +1200, Regan Heath wrote:
>
>> Sub-thread for discussions
>
>What are the problems that this discussion is trying to solve?
>
>I submit these problems:
>
>Problem A: How does one write code to tell readers of source code (people and IDEs) which module a referred to member exists in?
>
>  Example:
>      import aaa;
>      import bbb;
>      ...
>      funcA(); // Is this in aaa, bbb, or the current module?
>
>Problem B: How does one disambiguate identically named accessible members that happen to be in the same scope and whose signatures match?
>
>      import aaa;
>      import bbb;
>      ...
>      funcB(); // Is this calling the funcB in aaa, or the one in bbb?
>
>Problem C: How does one force the use of Fully Qualified Name references?
>
>There exists in the current D, techniques to solve problems A and B, but not C.
>
>One can use a simple Fully Qualified Name syntax or the alias syntax to
>resolve (A) and (B).
>
>      import aaa;
>      import bbb;
>      aaa.funcA();
>      bbb.funcB();
>
>or
>
>      import aaa;
>      import bbb;
>      alias aaa.funcA funcA;
>      alias bbb.funcB funcBb;
>      alias aaa.funcB funcBa;
>      ...
>      funcA;
>      funcBb();
>
>But why would these solutions need improving?
>
>I think it is because they are not efficient from a code writer and reader's point of view. If one is forced to always use FQN, it increases the burden on coders and readers because there are more characters to process and much of those are redundant. The alias technique is a partial solution as it significantly reduces clutter, but it is still not optimal because it causes a redundant typing and reading to be done, and has the potential to increase maintenance costs if the alias and import statements are separated in the source code.
>
>A better syntax would remove redundancy and localize the declarations for easier maintenance. Additional constraints would be to minimize the introduction of new keywords so as to avoid clashing with existing source code, and to permit backward compatibility for existing programs.
>
>      import in std.string
>              split,
>              find alias find_str;
>      import in bbb
>              find alias find_re;
>      ...
>      split(. . .);
>      find_re(. . .);
>
>The "in" keyword would signal to the compiler that restricts imported names to only the list of members named after the module, and thus all other public members in the module are not allowed to be accessed. The "alias" keyword gives the imported member a new identity within the current module.
>
>In order to force the use of FQN, an additional qualifier, or form of the import statement, is required because this is independent of the restricted importing concept.
>
> static import std.string; // All references must use FQN.
> static import in std.regexp find; // Only 'find' is imported and needs a
>FQN.
> static import in std.stdio writefln alias print;
>
> . . .
> std.string.find( . . . );  // okay
> std.regexp.find( . . . );  // okay
> std.regexp.replace(. . .); // not okay (name not imported).
> std.stdio.print(. . .);  // okay
> print(. . .) // not okay as static import requested.
>
>
>The "static" keyword (yes I know ... another overloading ... sigh) tells
>the compiler that the names (and any aliases) in the imported module are
>fixed (static) and must be referenced via a FQN.
>
>A possible further optimization to support FQN would be to allow an alias for the *package.module* name as a whole to allow easier maintenance.
>
> static import std.string alias s;
> import std.regexp alias r;
>
> s.replace( . . . );
> r.replace( . . . );
>
>
>All these forms could be used in conjunction with each other and with existing code.
>
>IMPORT :: [static] import MODULENAMELIST;
>MODULENAMELIST :: IMPORTMODULE [, MODULENAMELIST]
>IMPORTMODULE :: [in] MODULENAME [alias ID] [MEMBERLIST]
>MEMBERLIST :: IMPORTMEMBER [, MEMBERLIST]
>IMPORTMEMBER:: ID [alias ID]
>
>
>-- 
>Derek
>(skype: derek.j.parnell)
>Melbourne, Australia
>"Down with mediocrity!"
>10/07/2006 12:33:51 PM

This idea is very close to what Python do. I like it. It seems readable, understandable and looks like D code.
>      import in std.string
>              split,
>              find alias find_str;
>      import in bbb
>              find alias find_re;

In Python one would write :
from std.string import split, find as find_str
from bbb import find as find_re

Fully qualified name is the default in Python. If one want to import all the
symbols of a module :
from a.module import *
That is not considered as a best practice.

Derek's proposal would make FQN as optional. If we prefer to make FQN mandatory,
we will have to find something to express the import of all the symbol of a
module, a kind of shortcut.
Justin (jcc7) idea of replacing alias with = is also interesting.
There was also a proposal to use the 'with' keyword...

Ok. So here is my new mix of ideas :
1) Fully Qualified Name is the default.
2) Change the keywords a little bit :
import my.package.module ; // The symbols of this package have to be fully
qualified.
import with std.string  split, find = find_str ; // Nothing new,
import with std.regexp find = find_re ; // keyword have just changed.
Or we can keep the alias keyword :
import with std.string  split, find alias find_str ;
import with std.regexp find alias find_re ;
3) Allow the import of all the symbol of a module in the current namespace.
import with std.stdio in this ;

In brief, nothing new except the FQN is mandatory. One have to import all the symbols of a module in the current namespace to get the import behaviour we currently have.





July 10, 2006
Oskar Linde wrote:
> 2 comments:
> 
> Dave skrev:
>> Regan Heath wrote:
>>> Sub-thread for ideas.
>>
>> My preference for syntax and semantics would be:
>>
>> // regular import w/ optional alias (*)
>> import std.stdio, std.c.stdlib as stdlib, std.date;
>>
>> // symbol specific lookup w/ optional alias (**)
>> import from std.stdio writef, writefln as print, readf;
> 
> How would the parser disambiguate the import of the module readf from the symbol readf in std.stdio?

Great questions <g>

Assuming just the import from above, it wouldn't have to. Assuming std.stdio was imported in toto somwhere else, I would say that the specific import would then take precedence.

For example,

import std.stdio;
import std.mystdio; // also contains implements writefln
import from std.stdio writefln;
void main()
{
writefln("Always uses writefln from the 3rd import statement.");
std.mystdio.writefln("Unless an FQN is used.");
std.stdio.writefln("Same here.");
}

Because the whole point is to specifically disambiguate the writefln from the third import statement.

If the writefln from the 3rd statement was then considered part of the current namespace (module) as I believe was mentioned in the original threads, then this should not be a problem for the current compiler implementation either, because the innermost namespace takes precedence for lookup when an FQN is not used and there is a 'conflict'.

> 
>> // combined
>> import std.stdio, from std.thread wait as wait, pause, std.date as date;
>>
>> * Importing an entire module as an alias would then require a FQN to access any of the members, for example given the above:
>> malloc(...); // error
>> std.c.stdlib.malloc(...); // error
>> stdlib.malloc(...); // Ok
>>
>> ** A specific symbol that was not aliased would require a FQN to access. For example from above:
>> std.stdio.writef(...);
> 
> Just to clarify, does this mean that to import std.c.stdlib as FQN only, you would have to do:
> 
> import std.c.stdlib as std.c.stdlib; //(1)
> 

No, that would cause a conflict with the way alias works already because you can't:

alias std.c.stdlib std.c.stdlib;

I would say then that the coder would just FQN everything themselves (even though it's not required by the compiler) or come up with an alias like:

import std.c.stdlib as std_c_stdlib;

I'm trying to make this all implementable with what's aleady in the compiler except for how the new import syntax is processed, and be backward compatible with the current import statement.

> Or, indirectly:
> 
> import from std.c.stdlib a_symbol_I_will_never_use;
> 

That would limit the imports from std.c.stdlib to 'a_symbol_I_will_never_use' (unless other import statements imported other symbols from std.c.stdlib of course). So, if the specific symbol 'a_symbol_I_will_never_use' wasn't ever used, the import statement would be superfluous.

> ?
> 
> 1) When you import something as fqn-only, you must be ok with typing out the full name quite a lot, so once more will not make much of a difference I guess...
> 
> /Oskar
July 10, 2006
On Mon, 10 Jul 2006 05:37:41 +0000 (UTC), Tyro <Tyro_member@pathlink.com> wrote:
> In article <optcgh6iaa23k2f5@nrage>, Regan Heath says...
>>
>> On Mon, 10 Jul 2006 03:43:07 +0000 (UTC), Tyro <Tyro_member@pathlink.com>
>> wrote:
>>> In article <optcgd2ruo23k2f5@nrage>, Regan Heath says...
>>>>
>>>> More random musings.. if we have a syntax that reads:
>>>>
>>>> import std.stdio as foo;
>>>>
>>>> which imports the symbols in std.stdio into a namespace called 'foo',
>>>> preventing access as just "writefln" requiring "foo.writefln" then what
>>>> happens in a case like this:
>>>>
>>>> --[a.d]--
>>>> import std.stdio;
>>>> template foo { writefln("Hello World"); }
>>>>
>>>> --[b.d]--
>>>> import std.stdio as foo;
>>>>
>>>> void main() {
>>>>   mixin foo;
>>>> }
>>>>
>>>> ?
>>>>
>>>> Regan
>>>
>>>
>>> Well, as far as I can see you should get an error telling you that foo
>>> is not a
>>> template. Now if you meant for the first line in [b.d] to read:
>>>
>>> import a as foo;
>>>
>>> Then I think we have a different issue. But since the programmer when
>>> through
>>> the trouble of importing into a named scope, this should be a no braner.
>>> In
>>> order to access the foo template , he needs to code explicitly.
>>>
>>> :--[b.d]--
>>> :import a as foo;
>>> :
>>> :void main() {
>>> :   mixin foo;     // Error, foo is not a template
>>> :   mixin foo.foo; // Ok
>>> :}
>>
>> Ooops, my bad example, what I meant was:
>>
>> --[a.d]--
>> import std.stdio;
>> template foo { writefln("Hello World"); }
>>
>> --[b.d]--
>> import std.stdio as bar;
>> import a;
>>
>> void main() {
>>   mixin foo;
>> }
>>
>> I accidently called the template and import named scope the same thing.
>>
>> Regan
>
> In this case I assume that you are concerned with conflicts that may be
> generated between both imports of std.stdio in [a.d] and [b.d].

Nope. In a.d you can call "writefln" but in b.d you must call "bar.writefln", plain old writefln will fail, right?
So, what does this mean for mixins?

Regan
July 11, 2006
Regan Heath wrote:
> 1. starting with hello world, eg.
> 
> --[helloworld.d]--
> import std.stdio;
> void main()
> {
>   writefln("Hello World");
> }
> 
> 2. Moving on to import his own module eg.
> 
> --[mymod.d]--
> void sayHello() { writefln("Hello World"); }
> 
> --[helloworld.d]--
> import std.stdio,mymod;
> void main()
> {
>   sayHello();
> }
> 
> which will all work fine, no problems.
> 
> 3. It's when he starts to think.. "I just need writefln why not code it like this" ..
> 
> --[mymod.d]--
> void sayHello() { writefln("Hello World"); }
> 
> --[helloworld.d]--
> import std.stdio.writefln,mymod;
> void main()
> {
>   sayHello();
> }
> 
> that he'll get an error.

Of course he gets an error. How are you supposed to compile these modules separately (ie. dmd -c mymod.d). It should be:

[mymod.d]
import std.stdio.writefln;
void sayHello() { writefln("Hello World"); }

[helloworld.d]
import mymod;
// or even: import mymod.sayHello;

void main()
{
  sayHello();
}

If you think of mymod.d as a library, how can it depend on an import statement on a "client" module that is not possibly even implemented yet?

-- 
Jari-Matti
July 11, 2006
Regan Heath wrote:
> On Mon, 10 Jul 2006 05:37:41 +0000 (UTC), Tyro <Tyro_member@pathlink.com> wrote:
>> In article <optcgh6iaa23k2f5@nrage>, Regan Heath says...
>>>
>>> --[a.d]--
>>> import std.stdio;
>>> template foo { writefln("Hello World"); }
>>>
>>> --[b.d]--
>>> import std.stdio as bar;
>>> import a;
>>>
>>> void main() {
>>>   mixin foo;
>>> }
>>>
>>> I accidently called the template and import named scope the same thing.
>>>
>>> Regan
>>
>> In this case I assume that you are concerned with conflicts that may be generated between both imports of std.stdio in [a.d] and [b.d].
> 
> Nope. In a.d you can call "writefln" but in b.d you must call
> "bar.writefln", plain old writefln will fail, right?
> So, what does this mean for mixins?

Are you referring to the existing functionality (sans implementation bugs) or some specific proposal here. IMO Tyro is right and plain old writefln definitely shouldn't fail here. It should only fail when std.stdio has been imported privately in file a.d. But if import were private by default, then the compiler would say 'b.d(5): undefined identifier writefln'.

-- 
Jari-Matti
July 11, 2006
On Tue, 11 Jul 2006 23:46:03 +0300, Jari-Matti Mäkelä <jmjmak@utu.fi.invalid> wrote:
> Regan Heath wrote:
>> On Mon, 10 Jul 2006 05:37:41 +0000 (UTC), Tyro <Tyro_member@pathlink.com> wrote:
>>> In article <optcgh6iaa23k2f5@nrage>, Regan Heath says...
>>>>
>>>> --[a.d]--
>>>> import std.stdio;
>>>> template foo { writefln("Hello World"); }
>>>>
>>>> --[b.d]--
>>>> import std.stdio as bar;
>>>> import a;
>>>>
>>>> void main() {
>>>>   mixin foo;
>>>> }
>>>>
>>>> I accidently called the template and import named scope the same thing.
>>>>
>>>> Regan
>>>
>>> In this case I assume that you are concerned with conflicts that may be
>>> generated between both imports of std.stdio in [a.d] and [b.d].
>>
>> Nope. In a.d you can call "writefln" but in b.d you must call
>> "bar.writefln", plain old writefln will fail, right?
>> So, what does this mean for mixins?
>
> Are you referring to the existing functionality (sans implementation
> bugs) or some specific proposal here.

The proposal to import a module into a namespace. In this case "std.stdio" into "bar", meaning "writefln" does not exist but "bar.writefln" does.

> IMO Tyro is right and plain old
> writefln definitely shouldn't fail here. It should only fail when
> std.stdio has been imported privately in file a.d. But if import were
> private by default, then the compiler would say 'b.d(5): undefined
> identifier writefln'.

All I was really asking was...

If module a.d imports "std.stdio" into the current namespace, then calls "writefln" in a template which is then mixed into another source file b.d, which imports "std.stdio" into a named namespace "bar", can that template call "writefln"?

I suspect the answer would have to be "no". After all the mixin is mixed into the scope of b.d, it doesn't exist in the scope of a.d where "writefln" is valid.

All I was really trying to do was raise this as an issue which would occur (and need a solution) if we had import into namespace 'x'.

Regan

July 11, 2006
Regan Heath wrote:
> On Tue, 11 Jul 2006 23:46:03 +0300, Jari-Matti Mäkelä <jmjmak@utu.fi.invalid> wrote:
>> Regan Heath wrote:
>>> On Mon, 10 Jul 2006 05:37:41 +0000 (UTC), Tyro
>>> <Tyro_member@pathlink.com> wrote:
>>>> In article <optcgh6iaa23k2f5@nrage>, Regan Heath says...
>>>>>
>>>>> --[a.d]--
>>>>> import std.stdio;
>>>>> template foo { writefln("Hello World"); }
>>>>>
>>>>> --[b.d]--
>>>>> import std.stdio as bar;
>>>>> import a;
>>>>>
>>>>> void main() {
>>>>>   mixin foo;
>>>>> }
>>>>>
>>>>> I accidently called the template and import named scope the same thing.
>>>>>
>>>>> Regan
>>>>
>>>> In this case I assume that you are concerned with conflicts that may be generated between both imports of std.stdio in [a.d] and [b.d].
>>>
>>> Nope. In a.d you can call "writefln" but in b.d you must call
>>> "bar.writefln", plain old writefln will fail, right?
>>> So, what does this mean for mixins?
>>
>> Are you referring to the existing functionality (sans implementation bugs) or some specific proposal here.
> 
> The proposal to import a module into a namespace. In this case "std.stdio" into "bar", meaning "writefln" does not exist but "bar.writefln" does.

But if you import both:
 import std.stdio as bar;
 import std.stdio; (it's a public import, isn't it)

which (AFAIK) is exactly the same as

module a:
 import std.stdio;
module b:
 import a;
 import std.stdio as bar;

Doesn't that mean that you can use both:
[1]  writefln(...);
and
[2]  bar.writefln(...);
?

Which one of the proposals makes it illegal to call [1] in module b?

> All I was really asking was...
> 
> If module a.d imports "std.stdio" into the current namespace, then calls "writefln" in a template which is then mixed into another source file b.d, which imports "std.stdio" into a named namespace "bar", can that template call "writefln"?
> 
> I suspect the answer would have to be "no".

Um, I don't think so. The only thing that could make it illegal is that
 template foo { writefln("Hello World"); }
isn't valid D. Maybe it should be
 template foo { void hello() { writefln("Hello World"); } }
?

> After all the mixin is mixed
> into the scope of b.d, it doesn't exist in the scope of a.d where
> "writefln" is valid.

Correct. From http://www.digitalmars.com/d/mixin.html: "Unlike a template instantiation, a template mixin's body is evaluated within the scope where the mixin appears, not where the template declaration is defined. It is analogous to cutting and pasting the body of the template into the location of the mixin."

> All I was really trying to do was raise this as an issue which would occur (and need a solution) if we had import into namespace 'x'.

I think a more interesting use case would be to use real templates (they're instantiated in module a) instead of mixins and module a that is using a import statement that only imports some of the std.stdio members. That might even cause some trouble?

-- 
Jari-Matti