Bug 13855 – Allow multiple selective imports from different modules in a single import statement
Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-12-11T21:52:17Z
Last change time
2018-02-27T17:06:10Z
Assigned to
No Owner
Creator
Martin Nowak
Comments
Comment #0 by code — 2014-12-11T21:52:17Z
Couldn't we just allow this?
import std.algorithm : copy, uniq, std.range : walkLength;
This is one of (very few) grammar warts that regularly annoy me, because I have to make it 2 lines
import std.algorithm : copy, uniq;
import std.range : walkLength;
or import one module completely.
import std.algorithm, std.range : walkLength;
Sure this would make the grammar slightly more ambiguous, but when peeking a few tokens ahead you can always make a decision. We'd just have to disallow that selective imports are followed by non-selective ones.
Especially since we found out that static and selective imports could be done lazily (bug 13255), it would be nice.
------
To defeat a few expected counter-arguments upfront.
- "One line more or less doesn't matter."
This is a function that we recently added to dub [1].
auto fuzzySearch(R)(R strings, string input){
import std.algorithm : levenshteinDistance, schwartzSort, partition3;
import std.traits : isSomeString;
import std.range : ElementType;
static assert(isSomeString!(ElementType!R), "Cannot call fuzzy search on non string rang");
immutable threshold = input.length / 4;
return strings.partition3!((a, b) => a.length + threshold < b.length)(input)[1]
.schwartzSort!(p => levenshteinDistance(input.toUpper, p.toUpper));
}
It could be written like so.
auto fuzzySearch(R)(R strings, string input){
import std.algorithm : levenshteinDistance, schwartzSort, partition3,
std.traits : isSomeString, std.range : ElementType;
static assert(isSomeString!(ElementType!R), "Cannot call fuzzy search on non string rang");
immutable threshold = input.length / 4;
return strings.partition3!((a, b) => a.length + threshold < b.length)(input)[1]
.schwartzSort!(p => levenshteinDistance(input.toUpper, p.toUpper));
}
Much better signal/noise ratio.
- "You could import the complete module because that's already local import."
Often you don't want to pollute your scope, even a small one.
At least I intuitively thinks of importing as an expenditure that should be minimized. If we make selective imports lazy that would actually be true.
Also selective imports self-document where you got a certain symbol.
[1]: https://github.com/D-Programming-Language/dub/blob/c67201d663f3cf832feed1c3894c3aec328fec1a/source/dub/internal/utils.d#L233
Comment #1 by code — 2014-12-11T21:54:22Z
(In reply to Martin Nowak from comment #0)
> Much better signal/noise ratio.
Well not without syntax highlighting in Bugzilla, but if you copy that code into your editor you can see it.
Comment #2 by bearophile_hugs — 2014-12-11T21:59:33Z
(In reply to Martin Nowak from comment #0)
> Couldn't we just allow this?
>
> import std.algorithm : copy, uniq, std.range : walkLength;
>
> This is one of (very few) grammar warts that regularly annoy me, because I
> have to make it 2 lines
>
> import std.algorithm : copy, uniq;
> import std.range : walkLength;
I prefer the version on two lines, because it's not easy for the eye to tell apart the ":" from the ",".
Comment #3 by code — 2014-12-12T00:32:14Z
(In reply to bearophile_hugs from comment #2)
> (In reply to Martin Nowak from comment #0)
> > Couldn't we just allow this?
> >
> > import std.algorithm : copy, uniq, std.range : walkLength;
> >
> > This is one of (very few) grammar warts that regularly annoy me, because I
> > have to make it 2 lines
> >
> > import std.algorithm : copy, uniq;
> > import std.range : walkLength;
>
> I prefer the version on two lines, because it's not easy for the eye to tell
> apart the ":" from the ",".
I do agree with you, although most module names have a dot in the middle, and syntax highlighting could take of the rest.
But your argument actually misses the point. Imports are on the noise side of S/N ratio when reading code. They can usually be skipped unless one actually wants to know where a symbol comes from.
So in the above example, a seasoned programmer will already know what partion3, schwarzSort, levenshteinDistance, and toUpper do and can focus on the complex parts.
Comment #4 by bugzilla — 2017-03-07T10:32:44Z
> Much better signal/noise ratio.
That's clearly a matter of opinion, as I find the multi-line import much more legible.
Comment #5 by code — 2017-03-14T09:40:40Z
Well the point was that imports don't need to be that legible, because they hardly are relevant during reading.
Java is famous for it's horrible import "manifests", many editors just fold those blocks.
My main motivation is the following.
Whenever I have the following import (valid today)
import std.file, std.stdio : writeln;
and I need sth., say map, I can either prepend all of std.algorithm or add another selective import.
void foo(Range r)
{
import std.file, std.stdio : writeln;
import std.algorithm : map;
writeln(r.map!readText);
}
And often import std.algorithm, std.file, std.stdio : writeln; is the much more concise choice.
Also I'm usually sorting imports, which isn't possible when only the last one supports selective imports.
Comment #6 by yebblies — 2017-03-14T13:15:15Z
My opinion is that this is a language fix for a style issue, and would not pull its weight.
While we certainly do want to avoid the issue java has, we already do avoid most of that with the existing syntax.
For your 'signal/noise ratio' example, I consider the first example less noisy as the 'import' makes it clear what those lines are.
I personally only ever import from one module per line, and that seems to avoid the problems you're encountering.
In light of the recent move towards local imports vs. global imports, I'm starting to think that what we *really* want is for the compiler to *automate* imports for us.
It's analogous to the old days with (early versions of) C, where you have to declare all local variables at the top of the function. C++ improved on this by allowing variable declarations closer to where they're actually used (I'm not sure if this is standard C, but recent implementations like gcc seem to have also adopted this in C code). This is like moving imports from module scope to local scope.
However, C++ (well, earlier versions of it) still suffered from having to type incredibly long type names, like:
------
std::vector<int>::iterator it = std::vector<int>::iterator::end;
------
D improved upon this by allowing the LHS type name to be elided where the type can be inferred by the compiler. Most of the time, this is possible, so in modern idiomatic D code these days, using `auto x = ...` is commonplace and greatly improves legibility.
What if we had an analogous "import inference", where you don't need to spell out long complicated module names just to be able to use some symbol in some module std.abc.xyz? Let the compiler figure it out for us.
This is still a raw initial idea, so it will need further refinement, but my thought is something along these lines: suppose we introduced a "lazy import" construct:
------
auto myFunc(Args...)(Args args) {
lazy import std.algorithm;
return args.map!(x => x*2).filter!(x < 100);
}
------
The lazy import tells the compiler that if undefined symbols are encountered, it should look into std.algorithm to see if the symbol is defined there. If it is, implicitly import the module that it's defined in, and keep going. Otherwise emit an error message.
If there is any ambiguity that arises, e.g., both std.algorithm.foo.bar and std.algorithm.baz.bar exist, and `bar` is referred to, then also emit an error, and require disambiguation using the usual mechanisms (e.g., naming `foo.bar` or `baz.bar`).
If a recent proposal for `import std;` gets accepted, then we could even have:
------
lazy import std; // I'm lazy, just lookup all symbols in Phobos for me
... // use std.*.* to your heart's content
------
This could be placed either in module scope or in local scope, depending on just how lazy you are. It kills all boilerplate and verbosity by reducing import statements to their bare minimum.
Comment #9 by github-bugzilla — 2017-12-26T18:28:38Z