www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - No way to selectively expose imported symbols using 163

reply Sean Kelly <sean f4.ca> writes:
Consider this code:

     module a;

     void afn1() {}
     void afn2() {}
--------
     module b;

     public import a;

     void bfn1() {}
--------
     module main;

     import a;
     import b;

     void main()
     {
         afn1();
         afn2();
         bfn1();
     }

This is the classic method of exposing symbols from a to users who 
import b.  In this case let's assume that b uses some aspects of a and 
it's unreasonable to expect the user to import a just to use b.  So far 
so good.  But what if a is a large module and the programmer doesn't 
want to expose all of a through b?  Using the new import features, one 
might assume that it is possible to change b to this:

     module b;

     public import a : afn1;

     void bfn1() {}

However, doing so results in this compile error:

     C:\code\src\d\test>dmd main a b
     a.d(3): function a.afn1 conflicts with b.afn1 at b.d(3)

I suppose this should be expected as the old private import + alias 
method would have produced the same error, but it's still irritating. 
Now what if we make the import private:

     module b;

     private import a : afn1;

     void bfn1() {}

This results in the same compile error as public import:

     C:\code\src\d\test>dmd main a b
     a.d(3): function a.afn1 conflicts with b.afn1 at b.d(3)

The error with private imports is to be expected given the lookup rules 
in D, but it's worth noting nevertheless.  So at this point I must 
conclude that, given the current implementation:

     * Selective public import is useless for library developers.
       Renaming symbols in this case defeats the purpose of publicly
       importing, and it is unreasonable to assume that the user will not
       import both a and b in his own code.

     * Selective private import is useless for library developers without
       diligent and careful use of the renaming feature, because the risk
       of symbol collisions is not only just as great as with public
       import, but the fact that the import is private actually increases
       the likelihood that the user will import both a and b in his own
       code.

The simplest solution would be to change selective import to not bind 
the symbols into the current namespace, thus keeping the symbols as 
"second class citizens."  This would have the following effect:

     import std.stdio : writef;

Import one symbol, writef, as a second-class citizen for the current module.

     static import std.stdio : writef;

Make the symbol 'writef' available to the current module via its fully 
qualified name only.

     import std.stdio : writef = writef;

Import only writef from std.stdio and bind it into the current scope. 
This would be equivalent to the current behavior of the non-renaming 
selective import.  Thus, currently, these two lines are identical:

     static import std.stdio : writef;
     import std.stdio : writef;

But under the new method, these two lines would be identical:

     static import std.stdio : writef = writef;
     import std.stdio : writef = writef;

This should be obvious, as the "= writef" suggests binding the symbol 
into the current scope using the name "writef".  Please note that:

     import std.stdio : writef = writef;

Should not create a "second class" lookup table for the symbol, 
"writef", as the "= writef" is present.  If both the binding and 
second-class lookup is desired, a two-line approach may be used:

     import std.stdio : writef;
     alias std.stdio.writef writef;

I don't foresee anyone actually wanting to do this, but I thought it was 
worth mentioning anyway.

Comments?


Sean


P.S. I would have posted this sooner, but I've been on vacation for the 
past two weeks and didn't have time to play with 163 until this morning.
Jul 27 2006
parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Sean Kelly wrote:
 Consider this code:
[...]
 
 The simplest solution would be to change selective import to not bind 
 the symbols into the current namespace, thus keeping the symbols as 
 "second class citizens."  This would have the following effect:
 
     import std.stdio : writef;
 
 Import one symbol, writef, as a second-class citizen for the current 
 module.
 
[...]
 
     import std.stdio : writef = writef;
 
 Import only writef from std.stdio and bind it into the current scope. 
 This would be equivalent to the current behavior of the non-renaming 
 selective import.  Thus, currently, these two lines are identical:
 
     static import std.stdio : writef;
     import std.stdio : writef;
 
 But under the new method, these two lines would be identical:
 
     static import std.stdio : writef = writef;
     import std.stdio : writef = writef;
 
 This should be obvious, as the "= writef" suggests binding the symbol 
 into the current scope using the name "writef".  Please note that:
 
     import std.stdio : writef = writef;
 
 Should not create a "second class" lookup table for the symbol, 
 "writef", as the "= writef" is present.  If both the binding and 
 second-class lookup is desired, a two-line approach may be used:
 
     import std.stdio : writef;
     alias std.stdio.writef writef;
 
 I don't foresee anyone actually wanting to do this, but I thought it was 
 worth mentioning anyway.
 
 Comments?
 
 
 Sean
 
 
Yes, I agree that selective import should import the names as "second class citizens" (or "to the secondary namespace" as Walter calls it (or even "background namespace")) : import std.stdio : writef; A selective import with renaming, conversely, should then create a "first class citizen" (aka "primary namespace"?, "foreground namespace"): import std.stdio : writef = writef; You initial code example shows quite well why this is important. --- What I don't agree is about that static+selective import stuff: static import std.stdio : writef; That is, the idea that static(FQN) and selective import should work together. I'm not fond the idea that std.stdio, a module, is "available" but is not complete. What is the use and sense of that? Why could it not be a regular(non-selective) FQN import? That being, if a static(FQN) import is to not work together with a selective import, then such syntax should be a compiler error, instead of just working like a normal import: static import foo.bar : bar; // Error Static(FQN) with renaming import should be an error too: static import foo.bar = baz; // Error -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 30 2006
parent reply Sean Kelly <sean f4.ca> writes:
Bruno Medeiros wrote:
 
 What I don't agree is about that static+selective import stuff:
   static import std.stdio : writef;
 
 That is, the idea that static(FQN) and selective import should work 
 together. I'm not fond the idea that std.stdio, a module, is "available" 
 but is not complete. What is the use and sense of that? Why could it not 
 be a regular(non-selective) FQN import?
Consistency? To me, "static import std.stdio : writef" says "make only writef available from std.stdio and only as a FQN." Sean
Jul 30 2006
parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Sean Kelly wrote:
 Bruno Medeiros wrote:
 What I don't agree is about that static+selective import stuff:
   static import std.stdio : writef;

 That is, the idea that static(FQN) and selective import should work 
 together. I'm not fond the idea that std.stdio, a module, is 
 "available" but is not complete. What is the use and sense of that? 
 Why could it not be a regular(non-selective) FQN import?
Consistency? To me, "static import std.stdio : writef" says "make only writef available from std.stdio and only as a FQN." Sean
Consistency? I was talking more about practical uses. Under my suggestion that syntax would simply not be allowed, so there is no worry about what it does. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 31 2006