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