DIP16: Transparently substitute module with package

Jonathan M Davis jmdavisProg at gmx.com
Fri Mar 30 14:35:25 PDT 2012


On Friday, March 30, 2012 14:33:58 Andrei Alexandrescu wrote:
> On 3/30/12 1:39 PM, Jonathan M Davis wrote:
> > However, I'm very nervous about the second part. e.g. std.sort instead of
> > std.algorithm.sort seems like a bad idea to me. It increases the odds of
> > name conflicts for little benefit.
> 
> Example?

std.sort works because there's only one sort. If there are two, you get a 
conflict (e.g. if you had std.path.sort which sorted paths in some path-specific 
manner). If std.path.sort existed now, then std.sort wouldn't work, and you'd 
be forced to specify std.algorithm.sort or std.path.sort, and that's fine. It 
would be similar to having to specify std.algorithm.indexOf when you've 
imported both std.string and std.algorithm. But the problem is when 
std.path.sort is added _later_.

All of a sudden, code which used std.sort and worked is now broken. The 
problem does currently exist in that if we added indexOf to another module - 
say std.array - then code which imported either std.string or std.algorithm as 
well as std.array would break with the addition of std.array.indexOf, but your 
proposal makes it worse. Not only does it provide another way in which adding 
a function could result in conflicts when existing code is recompiled, but it 
makes it so that if you add any function anywhere in the _entire standard 
library_ which has the name as an existing one, you get a conflict (if anyone 
uses std.x rather than x or the full import path).

D does a good job of providing ways to fix name conflicts, but it doesn't do a 
good job of preventing them when adding new symbols to a library (primarily 
because it doesn't use static imports by default), and your proposal makes 
that part of the problem worse. If std.x were to become common practice, then 
any time that you added a symbol to a library when that symbol was already 
used by another module, you'd create conflicts (combined with the fact that 
private doesn't hide symbol names but merely makes them inaccessible, this 
could result in a lot of symbol name conflicts).

> > Not to mention, it'll make it a lot more confusing
> > to find what modules stuff is actually in if people start doing stuff like
> > 
> > std.sort(arr);
> > 
> > In the case of sort, you may know where it's from - particularly since
> > it's so common - but the less well-known the function is, the less likely
> > that is at all obvious where it comes from, and if you're dealing with
> > 3rd party software, then it wouldn't be at all obvious. For instance, how
> > would you know that party.foo is really party.bar.foo? You wouldn't.
> 
> Why should you?

Do you know what the foo function does? If you don't, you're going to have to 
look it up. And if you don't know what module it comes from, you can't do 
that. You also have to know where foo is from if a foo function is added to 
another module and causes a conflict, because you're going to have to give the 
full import path to actually use it. That's currently true with just bare foo 
as well, but party.foo gives the illusion of specifying where foo is from 
without actually specifying where it's from. At least right now, if foo is 
used with its import path, you know that that's actually its import path.

Also, what happens if we want to add a module named sort later? The fact that 
people are using std.sort means that adding std.sort as a module will break 
code. Granted, it's not very likely that we're going to add a module named 
sort, but there are plenty of other symbol names that it could happen with. 
But then again, if we decided to provide a module with all of the major sort 
algorithms, then maybe we _would_ create a module named std.sort. Just because 
we don't see a need now doesn't mean that we won't later. In either case, by 
allowing std.x where x is a symbol in any sub-module of std, you're going to 
create conflicts any time that you add a module which has the same name as an 
existing symbol anywhere in the library.

> > Being so lax about
> > importing could really harm code readibility (and maintainibility, since
> > it
> > increases the odds of name clashes). So, I'm inclined to say that that is
> > a
> > _bad_ idea.
> 
> Maybe if you produce a solid example, I'd be convinced.

Well, as I've pointed with a few examples here, your proposal will increase 
the chances of adding symbol conflicts any time that a symbol is added to a 
library all just so that you can do std.algorithm.sort instead of 
std.algorithm.submodule1.sort once sort has been moved to 
std.algorithm.sumodule1. And we could make it possible to do 
std.algorithm.sort without adding all of those possible conflicts.

The simplest solution would simply be to make it so that if std.algorithm.sort 
is used, and std.algorithm is a package with a std.algorithm.package module, 
then the compiler looks in all of the sub-modules of std.algorithm to find 
sort. That solves the problem right there without increasing the odds of 
symbol conflicts across the entire library like your proposal does.

But personally, I like the idea of making it so that publicly imported symbols 
can be accessed as if they were in the module that publicly imported them 
(with package.d being treated as if it had the same name as the package that 
it's in). That's essentially how it already works except when specifying the 
full import path for a symbol. And that way, you can specify in 
std.algorithm.package.d exactly what you want to be imported when 
std.algorithm is imported (including using : to restrict it to specific symbols 
in a module), and only those symbols will be treated as if they were part of 
std.algorithm - both for importing purposes and when specifying the import 
path when using a symbol. The library maintainer then has control over which 
symbols get used with which import paths.

- Jonathan M Davis


More information about the Digitalmars-d mailing list