www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Selective and renamed imports

reply Christian Kamm <kamm-incasoftware removethis.de> writes:
Hello fellow import-users,

I've tried to get bug 314 
(http://d.puremagic.com/issues/show_bug.cgi?id=314) fixed for a very long 
time now. There seems to be no progress and I suspect that the reason for it 
could be that Walter is unsure whether the import behavior I implemented in 
https://github.com/D-Programming-Language/dmd/pull/190 is desirable.

In this post I'll point out the controversy with selective imports and hope 
it'll spark some discussion.

First off, I think everyone agrees that this test case needs to start 
passing:

base.d --
int foo(int) { return 0; }

first.d --
import base : foo;

second.d --
import first;
static assert(!is(typeof(foo)));

It's the baseline for bug 314. No matter how the details are resolved, this 
needs to work. It's the big "D doesn't even get imports right" hole that 
makes this bug the most voted for issue on bugzilla.

Now, what's the problem? Overload resolution.

Currently, selectively importing a function or overload set also *merges it 
into the local module's overload set*. That has several problems. One is 
that changing an import to selective or back can change program behavior:

overloadproblem.d --
import base; // : foo;
long foo(long) { return 0; }
static assert(is(typeof(foo(0)) == long));

This code will fail to compile when you change the import to a selective 
one.

The second problem is that, since the base.foo got integrated into the 
overloadproblem.foo overload set, modules importing 'overloadproblem' are no 
longer able to honor import visibility:

importer.d --
import overloadproblem;
static assert(is(typeof(foo(0)) == long));

This will fail even though base.foo was imported privately. To resolve 
foo(0) to long overloadproblem.foo(long), import visibility would have to be 
considered during overload resolution to skip int base.foo(int). That's 
something Walter doesn't want to do.


These are the reasons why my pull request changes the selective import 
behavior to not merge overload sets. It makes 'import base : foo;' treat 
'foo' exactly the same way as 'import base;' would have done; it does not 
matter whether an symbol was imported selectively or not. You can get the 
old behavior by using an explicit alias.

Does this work as you'd expect? Please write a short reply even if you just 
agree. I hope the feedback will convince Walter to give this serious 
consideration. You can also let me know that this is totally bananas, of 
course.

Regards,
Christian
Oct 16 2011
next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 16.10.2011 14:08, Christian Kamm wrote:
 Hello fellow import-users,

 I've tried to get bug 314
 (http://d.puremagic.com/issues/show_bug.cgi?id=314) fixed for a very long
 time now. There seems to be no progress and I suspect that the reason for it
 could be that Walter is unsure whether the import behavior I implemented in
 https://github.com/D-Programming-Language/dmd/pull/190 is desirable.

 In this post I'll point out the controversy with selective imports and hope
 it'll spark some discussion.

 First off, I think everyone agrees that this test case needs to start
 passing:

 base.d --
 int foo(int) { return 0; }

 first.d --
 import base : foo;

 second.d --
 import first;
 static assert(!is(typeof(foo)));

 It's the baseline for bug 314. No matter how the details are resolved, this
 needs to work. It's the big "D doesn't even get imports right" hole that
 makes this bug the most voted for issue on bugzilla.

 Now, what's the problem? Overload resolution.

 Currently, selectively importing a function or overload set also *merges it
 into the local module's overload set*. That has several problems. One is
 that changing an import to selective or back can change program behavior:

 overloadproblem.d --
 import base; // : foo;
 long foo(long) { return 0; }
 static assert(is(typeof(foo(0)) == long));

 This code will fail to compile when you change the import to a selective
 one.

 The second problem is that, since the base.foo got integrated into the
 overloadproblem.foo overload set, modules importing 'overloadproblem' are no
 longer able to honor import visibility:

 importer.d --
 import overloadproblem;
 static assert(is(typeof(foo(0)) == long));

 This will fail even though base.foo was imported privately. To resolve
 foo(0) to long overloadproblem.foo(long), import visibility would have to be
 considered during overload resolution to skip int base.foo(int). That's
 something Walter doesn't want to do.


 These are the reasons why my pull request changes the selective import
 behavior to not merge overload sets. It makes 'import base : foo;' treat
 'foo' exactly the same way as 'import base;' would have done; it does not
 matter whether an symbol was imported selectively or not. You can get the
 old behavior by using an explicit alias.

 Does this work as you'd expect? Please write a short reply even if you just
 agree. I hope the feedback will convince Walter to give this serious
 consideration. You can also let me know that this is totally bananas, of
 course.

Yes, import != add a public alias from given module. Let them be consistent. P.S. I'm looking forward to that patch eventually getting merged. -- Dmitry Olshansky
Oct 16 2011
prev sibling next sibling parent Mike Wey <mike-wey example.com> writes:
On 10/16/2011 12:08 PM, Christian Kamm wrote:
 These are the reasons why my pull request changes the selective import
 behavior to not merge overload sets. It makes 'import base : foo;' treat
 'foo' exactly the same way as 'import base;' would have done; it does not
 matter whether an symbol was imported selectively or not. You can get the
 old behavior by using an explicit alias.

 Does this work as you'd expect? Please write a short reply even if you just
 agree. I hope the feedback will convince Walter to give this serious
 consideration. You can also let me know that this is totally bananas, of
 course.

 Regards,
 Christian

Yes, this is how i would expect it to work. -- Mike Wey
Oct 16 2011
prev sibling next sibling parent Jesse Phillips <jessekphillips+d gmail.com> writes:
On Sun, 16 Oct 2011 12:08:53 +0200, Christian Kamm wrote:

 Now, what's the problem? Overload resolution.
 
 Currently, selectively importing a function or overload set also *merges
 it into the local module's overload set*. That has several problems. One
 is that changing an import to selective or back can change program
 behavior:
 
 overloadproblem.d --
 import base; // : foo;
 long foo(long) { return 0; }
 static assert(is(typeof(foo(0)) == long));
 
 This code will fail to compile when you change the import to a selective
 one.
 
 The second problem is that, since the base.foo got integrated into the
 overloadproblem.foo overload set, modules importing 'overloadproblem'
 are no longer able to honor import visibility:
 
 importer.d --
 import overloadproblem;
 static assert(is(typeof(foo(0)) == long));
 
 This will fail even though base.foo was imported privately. To resolve
 foo(0) to long overloadproblem.foo(long), import visibility would have
 to be considered during overload resolution to skip int base.foo(int).
 That's something Walter doesn't want to do.
 
 
 These are the reasons why my pull request changes the selective import
 behavior to not merge overload sets. It makes 'import base : foo;' treat
 'foo' exactly the same way as 'import base;' would have done; it does
 not matter whether an symbol was imported selectively or not. You can
 get the old behavior by using an explicit alias.
 
 Does this work as you'd expect? Please write a short reply even if you
 just agree. I hope the feedback will convince Walter to give this
 serious consideration. You can also let me know that this is totally
 bananas, of course.
 
 Regards,
 Christian

Yes, this sounds like what is supposed to happen, and addresses some questions that have come up on D.learn.
Oct 16 2011
prev sibling next sibling parent Peter Alexander <peter.alexander.au gmail.com> writes:
On 16/10/11 11:08 AM, Christian Kamm wrote:
 <snip>
 Does this work as you'd expect? Please write a short reply even if you just
 agree. I hope the feedback will convince Walter to give this serious
 consideration. You can also let me know that this is totally bananas, of
 course.

 Regards,
 Christian

Yes, that's definitely how I would expect it to work.
Oct 16 2011
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2011-10-16 12:08, Christian Kamm wrote:
 Does this work as you'd expect? Please write a short reply even if you just
 agree. I hope the feedback will convince Walter to give this serious
 consideration. You can also let me know that this is totally bananas, of
 course.

 Regards,
 Christian

I agree. -- /Jacob Carlborg
Oct 16 2011
prev sibling next sibling parent "Nick Sabalausky" <a a.a> writes:
"Christian Kamm" <kamm-incasoftware removethis.de> wrote in message 
news:j7eahc$30ph$1 digitalmars.com...
 Hello fellow import-users,

 I've tried to get bug 314
 (http://d.puremagic.com/issues/show_bug.cgi?id=314) fixed for a very long
 time now. There seems to be no progress and I suspect that the reason for 
 it
 could be that Walter is unsure whether the import behavior I implemented 
 in
 https://github.com/D-Programming-Language/dmd/pull/190 is desirable.

 In this post I'll point out the controversy with selective imports and 
 hope
 it'll spark some discussion.

 First off, I think everyone agrees that this test case needs to start
 passing:

 base.d --
 int foo(int) { return 0; }

 first.d --
 import base : foo;

 second.d --
 import first;
 static assert(!is(typeof(foo)));

 It's the baseline for bug 314. No matter how the details are resolved, 
 this
 needs to work. It's the big "D doesn't even get imports right" hole that
 makes this bug the most voted for issue on bugzilla.

 Now, what's the problem? Overload resolution.

 Currently, selectively importing a function or overload set also *merges 
 it
 into the local module's overload set*. That has several problems. One is
 that changing an import to selective or back can change program behavior:

 overloadproblem.d --
 import base; // : foo;
 long foo(long) { return 0; }
 static assert(is(typeof(foo(0)) == long));

 This code will fail to compile when you change the import to a selective
 one.

 The second problem is that, since the base.foo got integrated into the
 overloadproblem.foo overload set, modules importing 'overloadproblem' are 
 no
 longer able to honor import visibility:

 importer.d --
 import overloadproblem;
 static assert(is(typeof(foo(0)) == long));

 This will fail even though base.foo was imported privately. To resolve
 foo(0) to long overloadproblem.foo(long), import visibility would have to 
 be
 considered during overload resolution to skip int base.foo(int). That's
 something Walter doesn't want to do.


 These are the reasons why my pull request changes the selective import
 behavior to not merge overload sets. It makes 'import base : foo;' treat
 'foo' exactly the same way as 'import base;' would have done; it does not
 matter whether an symbol was imported selectively or not. You can get the
 old behavior by using an explicit alias.

 Does this work as you'd expect? Please write a short reply even if you 
 just
 agree. I hope the feedback will convince Walter to give this serious
 consideration. You can also let me know that this is totally bananas, of
 course.

Yes, what Christian suggests is what I would expect. Imports should just be imports, whether selective or otherwise. Actually merging symbols into the current module is the job of alias, and if that's the behavior I wanted, than that's what I would have used: alias. If *certain* imports (ie, selective) are invading alias's territory, I'd classify that as either a bug or a design error.
Oct 16 2011
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/16/2011 12:08 PM, Christian Kamm wrote:
 Does this work as you'd expect? Please write a short reply even if you just
 agree. I hope the feedback will convince Walter to give this serious
 consideration. You can also let me know that this is totally bananas, of
 course.

 Regards,
 Christian

I agree. The current behavior is just useless, because it is never desirable or even acceptable to have implementation details leak into the public interface. Currently not even private import foo : bar; hides bar iirc. The sooner this gets merged the less code that inadvertently depends on undocumented symbols gets broken. Does public import foo : bar; work as expected with your pull request?
Oct 16 2011
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, October 17, 2011 00:17:56 Timon Gehr wrote:
 On 10/16/2011 12:08 PM, Christian Kamm wrote:
 Does this work as you'd expect? Please write a short reply even if you
 just agree. I hope the feedback will convince Walter to give this
 serious consideration. You can also let me know that this is totally
 bananas, of course.
 
 Regards,
 Christian

I agree. The current behavior is just useless, because it is never desirable or even acceptable to have implementation details leak into the public interface. Currently not even private import foo : bar; hides bar iirc.

That should be identical to import foo : bar; so if it one hid it and one didn't, that would _still_ be a problem. - Jonathan M Davis
Oct 16 2011
prev sibling parent reply Christian Kamm <kamm-incasoftware removethis.de> writes:
Timon Gehr wrote:
 Does
 
 public import foo : bar;
 
 work as expected with your pull request?

Yes. But it doesn't merge the symbol either, so if foo.bar is a function a.d -- public import foo : bar; void bar() {} b.d -- import a; // will only see the bar defined in a.d. Maybe public selective imports should actually behave the same way as aliases? Alternatively it could be an error: "'bar' hides publicly imported symbol of the same name"
Oct 17 2011
parent reply Kagamin <spam here.lot> writes:
Christian Kamm Wrote:

 Timon Gehr wrote:
 Does
 
 public import foo : bar;
 
 work as expected with your pull request?

Yes. But it doesn't merge the symbol either, so if foo.bar is a function a.d -- public import foo : bar; void bar() {} b.d -- import a; // will only see the bar defined in a.d. Maybe public selective imports should actually behave the same way as aliases? Alternatively it could be an error: "'bar' hides publicly imported symbol of the same name"

afaik currently all imports include symbols into the importing module. --- a.d --- int z; --- b.d --- import a; a.z is available as b.z now.
Oct 18 2011
parent Christian Kamm <kamm-incasoftware removethis.de> writes:
Kagamin wrote:
 afaik currently all imports include symbols into the importing module.
 
 --- a.d ---
 int z;
 
 --- b.d ---
 import a;
 a.z is available as b.z now.

No, this import does not introduce a new symbol b.z. It just adds a note to b's root scope saying "if a symbol isn't found here, check in a". It works differently for selective imports and that's cause for the problems with them: If you had done 'import a : z;' there would actually be a new symbol b.z.
Oct 18 2011
prev sibling parent Fawzi Mohamed <fawzi gmx.ch> writes:
I think that the treatment of normal and private imports in the patch =
proposed is the correct one.
Importing should be hidden, this is also basically what LDC 1 does, as =
it is much more strict on indirect imports.

Public imports on the other hand should be roughly equivalent to =
declaring the things in the module the imports them.
To be more precise they should be *exactly* equivalent to a private =
import + alias for all the imported things.

Being selective or renaming in my opinion should not be connected with =
outside visibility, that should be controlled with public/private.

There is indeed a difference between import and selective or renamed =
imports, and plain full module imports.
Those are imported with the goal of being used, thus one should check =
for collisions.
The solution is to make

	import x: y, a=3Dz;
	public import m1;
	public import m2: t, t2=3Dr;

which is equivalent to

	private import x: y, a=3Dz;
	public import m1;
	public import m2: t, t2=3Dr;

equivalent to

	import x;
	private alias x.y y;
	private alias x.z a;
	import m1;
	public alias m1.* *; // this is a shortcut to mean public =
aliases to all things declared in m1
	import m2;
	public alias m2.t t;
	public alias m2.r t2;

and privately exported symbols should never be visible when imported =
again.

I think that this is a semantic that makes sense, and looks natural, and =
piggybacks on already defined concepts.

Fawzi=
Oct 19 2011