November 18, 2020
On 18.11.20 02:11, Walter Bright wrote:
> On 11/15/2020 6:24 AM, Timon Gehr wrote:
>> Straw man.
> 
> Doing "import std;" is just asking for trouble. I argued against it for years, and yet it was slipped in a few months ago without my knowledge. Having everything in Phobos in scope is just causing trouble. For just one of the problems, it prevents adding new modules to Phobos as it can break existing code. For another, it turns the world's fastest compiler into a pig.
> 
> The example given was another example of why it's a bad idea, as the more names there are the more the likelihood of a collision.
> ...

There is no collision with proper overload resolution. The error is spurious.

>> I think it's safe to say that the snippet was written to illustrate a point and to focus on anything else to the detriment of a discussion of that point is just deflection.
> 
> Teoh would have never run into this issue if he hadn't used import std;
> 

I'm not so sure, it's likely that one would import both std.stdio and std.file as in my example below.

> I don't blame Teoh, a user should expect that import std; should not cause problems although the problems are inevitable. It should NEVER have been added to Phobos.
> ...

It's useful for scripting and, apparently, for exposing bugs in name lookup and overload resolution.

>> If the language was working properly, the code would compile and run fine. There is a hole in the design of the language here. There's no need to prioritize this particular issue, but I don't understand why you don't just acknowledge that this is not how the compiler should behave in this situation.
> 
> Frankly, I don't know how the compiler should behave. The original import scheme was simple, sound, and easy to explain, but everyone complained it wasn't "intuitive" and put in a complex scheme full of special cases. Complex special cases produce an endless stream of unanticipated special cases.
> ...

I don't think that's what happened in this case. There is the original simple, sound, easy to explain import scheme, but for mysterious reasons it was _never applied_ to nested imports. Why not?

What kinds of special cases have been added, by the way?

> Altering how the compiler does lookups now, regardless of how it "should" behave, could break existing code in mysterious ways. Especially if one imports every symbol in existence into a local scope :-(
> ...

I think that's not true in this case. (As I know you are aware, hijacking protection is designed precisely to avoid breaking code in mysterious ways!)

> 
>> It's not just rejects-valid either, this issue has accepts-invalid cases:
>>
>> ---
>> import std.stdio;
>>
>> string readAndLog(string filename){
>>      import std.file;
>>      auto text=readText(filename);
>>      write(filename," read successfully!\n");
>>      return text;
>> }
>>
>> void main(){
>>      writeln(readAndLog("important_data.txt"));
>> }
>> ---
> 
> Please elaborate on what is invalid about it? Don't make me guess!

It's a prototypical example of symbol hijacking. std.file.write hides std.stdio.write. Therefore, the code above overwrites the contents of the file "important_data.txt" with the string " read sucessfully!\n". The code should result in an ambiguity error as there are matches in two distinct overload sets.

I think it's explained in the issue I linked.
November 18, 2020
On Friday, 13 November 2020 at 22:57:03 UTC, H. S. Teoh wrote:
> Just ran into this today:
>
> 	void main() {
> 		int[string] aa;
> 		int x;
>
> 		x = aa.get("abc", 123);	// OK
>
> 		import std;
> 		x = aa.get("abc", 123);	// Error: template std.net.curl.get cannot deduce function from argument types ...
>
> 		import std, object;
> 		x = aa.get("abc", 123);	// OK
> 	}
>
> I know there's technically an explanation of this in terms of how the compiler implements import, but seriously, this is extremely annoying and confusing behaviour.  Especially because the symbol being hijacked comes from the implicitly-imported object.d.  Newbies wouldn't even *know* where to look if they encountered this error.
>
> D's import implementation was supposed to be designed to prevent symbol hijacking, but in this case it's falling flat on its face.
>
> Why can't we make it so that symbols that aren't explicitly named on the import line would add to existing overload sets instead of replacing them?
>
>
> T

This is why our style guide doesn't allow unqualified imports in functions. The more I think about it, the more I suspect function local imports were just a mistake to begin with.

Bouncing back from module scope into import scope is just fundamentally bad for understandability. The set of relevant symbols goes from "all the ones imported" to "all the ones in the module" back to "all the ones imported" again, with imports shadowing local functions, which is exactly the opposite of what should happen.

Putting aside how Walter is completely correct that "import std" was a mistake, I'd go further and also remove unqualified module imports completely. There's a set of features that are good for writing but bad for reading. Generally as time goes on, people discover those features are bad ideas, but because writing comes before reading, everyone thinks they're a good idea at first, so languages are doomed to reinvent them.
November 18, 2020
On Wednesday, 18 November 2020 at 10:14:44 UTC, FeepingCreature wrote:
> Putting aside how Walter is completely correct that "import std" was a mistake, I'd go further and also remove unqualified module imports completely. There's a set of features that are good for writing but bad for reading. Generally as time goes

Is it possible to imply an import by fully qualifying the path?

E.g.  ::statistics.stddev(numberslist)

November 18, 2020
On 18.11.20 11:14, FeepingCreature wrote:
> On Friday, 13 November 2020 at 22:57:03 UTC, H. S. Teoh wrote:
>> Just ran into this today:
>>
>>     void main() {
>>         int[string] aa;
>>         int x;
>>
>>         x = aa.get("abc", 123);    // OK
>>
>>         import std;
>>         x = aa.get("abc", 123);    // Error: template std.net.curl.get cannot deduce function from argument types ...
>>
>>         import std, object;
>>         x = aa.get("abc", 123);    // OK
>>     }
>>
>> I know there's technically an explanation of this in terms of how the compiler implements import, but seriously, this is extremely annoying and confusing behaviour.  Especially because the symbol being hijacked comes from the implicitly-imported object.d.  Newbies wouldn't even *know* where to look if they encountered this error.
>>
>> D's import implementation was supposed to be designed to prevent symbol hijacking, but in this case it's falling flat on its face.
>>
>> Why can't we make it so that symbols that aren't explicitly named on the import line would add to existing overload sets instead of replacing them?
>>
>>
>> T
> 
> This is why our style guide doesn't allow unqualified imports in functions. The more I think about it, the more I suspect function local imports were just a mistake to begin with.
> 
> Bouncing back from module scope into import scope is just fundamentally bad for understandability. The set of relevant symbols goes from "all the ones imported" to "all the ones in the module" back to "all the ones imported" again, with imports shadowing local functions, which is exactly the opposite of what should happen.
> ...

The shadowing is bad, not local imports...

> Putting aside how Walter is completely correct that "import std" was a mistake, I'd go further and also remove unqualified module imports completely. There's a set of features that are good for writing but bad for reading. Generally as time goes on, people discover those features are bad ideas, but because writing comes before reading, everyone thinks they're a good idea at first, so languages are doomed to reinvent them.

I'd hate that. It would make D less viable for writing small scripts.
If you are actively wondering, it's trivially easy to figure out where a symbol came from, and listing all imported symbols screws with reading as well as writing. (It's just bloat, nobody would actually look at the list, except when wondering where some specific symbol came from). The compiler should just ensure that there are no surprises, i.e., anti-hijacking is enough. If it makes sense to enforce additional style guidelines in your own code base, that's simple to do too, no need to break everyone else's code for no benefit.
November 18, 2020
On Friday, 13 November 2020 at 22:57:03 UTC, H. S. Teoh wrote:
>
> D's import implementation was supposed to be designed to prevent symbol hijacking, but in this case it's falling flat on its face.
>
> Why can't we make it so that symbols that aren't explicitly named on the import line would add to existing overload sets instead of replacing them?
>
>
> T

Wouldn't *that* allow hijacking? Unless of course you remove implicit conversions of parameters. If you have a `void foo(short)` in a module, and you're using it from your main like so: `foo(42);`, then suddenly, if overload sets were merged, another import at the same level could introduce `void foo(int)`, which would be a better match, and hijack the call, instead of creating an error as done currently.
November 18, 2020
On 18.11.20 17:23, Mathias LANG wrote:
> On Friday, 13 November 2020 at 22:57:03 UTC, H. S. Teoh wrote:
>>
>> D's import implementation was supposed to be designed to prevent symbol hijacking, but in this case it's falling flat on its face.
>>
>> Why can't we make it so that symbols that aren't explicitly named on the import line would add to existing overload sets instead of replacing them?
>>
>>
>> T
> 
> Wouldn't *that* allow hijacking? Unless of course you remove implicit conversions of parameters. If you have a `void foo(short)` in a module, and you're using it from your main like so: `foo(42);`, then suddenly, if overload sets were merged, another import at the same level could introduce `void foo(int)`, which would be a better match, and hijack the call, instead of creating an error as done currently.

How it should work:

- Do a lookup in the current module.
- If name is not found, figure out which (possibly nested) imports are in scope.
- Use the same standard overload resolution that is used for module-scope imports on those imports.
November 18, 2020
On Wednesday, 18 November 2020 at 10:14:44 UTC, FeepingCreature wrote:
> This is why our style guide doesn't allow unqualified imports in functions. The more I think about it, the more I suspect function local imports were just a mistake to begin with.

No way, they are correctness and performance feature. Look at STL to see what happens when everything is visible.
November 18, 2020
On Wed, Nov 18, 2020 at 10:12:25PM +0100, Timon Gehr via Digitalmars-d wrote: [...]
> How it should work:
> 
> - Do a lookup in the current module.
> - If name is not found, figure out which (possibly nested) imports are
> in scope.
> - Use the same standard overload resolution that is used for
> module-scope imports on those imports.

+1, this is the way it should have been from the beginning.


T

-- 
Why are you blatanly misspelling "blatant"? -- Branden Robinson
November 19, 2020
On Wednesday, 18 November 2020 at 21:13:00 UTC, Kagamin wrote:
> On Wednesday, 18 November 2020 at 10:14:44 UTC, FeepingCreature wrote:
>> This is why our style guide doesn't allow unqualified imports in functions. The more I think about it, the more I suspect function local imports were just a mistake to begin with.
>
> No way, they are correctness and performance feature. Look at STL to see what happens when everything is visible.

I think the problem with C++ is more transitive imports than file-level imports. Or rather, the lack of a module system.
November 20, 2020
On 11/17/2020 5:19 PM, Paul Backus wrote:
> On Wednesday, 18 November 2020 at 01:11:35 UTC, Walter Bright wrote:
>> Please elaborate on what is invalid about it? Don't make me guess!
> 
> Because std.file is imported locally, the call to write calls std.file.write instead of std.stdio.write, and overwrites important_data.txt.

Ok, and that's how scoped name resolution works. It's not invalid.


> If `import std.file` is moved to module scope, the compiler correctly diagnoses that the call is ambiguous.

Yes. This is why I asked, as I wondered if I was missing something.