www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Pathological import symbol shadowing

reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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

-- 
Recently, our IT department hired a bug-fix engineer. He used to work for
Volkswagen.
Nov 13 2020
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 13.11.20 23:57, 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.
 ...
Told you so. :P https://issues.dlang.org/show_bug.cgi?id=10378 https://issues.dlang.org/show_bug.cgi?id=17589
 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?
 
It should just use the standard import rules with overload sets. Then the code above would just work and no hijacking would be possible.
Nov 13 2020
parent reply Kagamin <spam here.lot> writes:
On Saturday, 14 November 2020 at 03:10:34 UTC, Timon Gehr wrote:
 It should just use the standard import rules with overload 
 sets. Then the code above would just work and no hijacking 
 would be possible.
You mean the code should not compile because symbols from different modules are in different overload sets? But then get from curl will always conflict with get in object, and thus will be unusable without renaming or FQN.
Nov 14 2020
parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 14 November 2020 at 14:13:32 UTC, Kagamin wrote:
 On Saturday, 14 November 2020 at 03:10:34 UTC, Timon Gehr wrote:
 It should just use the standard import rules with overload 
 sets. Then the code above would just work and no hijacking 
 would be possible.
You mean the code should not compile because symbols from different modules are in different overload sets? But then get from curl will always conflict with get in object, and thus will be unusable without renaming or FQN.
An ambiguity error only occurs if a matching function is found in both overload sets. [1] Since std.net.curl.get and object.get have completely different signatures, no function call will ever match both of them, so there will be no error. [1] https://dlang.org/spec/function.html#overload-sets
Nov 14 2020
parent reply Kagamin <spam here.lot> writes:
On Saturday, 14 November 2020 at 14:43:16 UTC, Paul Backus wrote:
 [1] https://dlang.org/spec/function.html#overload-sets
Ah, ok. foo(1L); // calls A.foo(long) This line looks incorrect. If there was no foo(long), foo(1L) would call foo(int), then if foo(long) appears, the call will be silently diverted.
Nov 14 2020
parent Paul Backus <snarwin gmail.com> writes:
On Saturday, 14 November 2020 at 15:48:47 UTC, Kagamin wrote:
 On Saturday, 14 November 2020 at 14:43:16 UTC, Paul Backus 
 wrote:
 [1] https://dlang.org/spec/function.html#overload-sets
Ah, ok. foo(1L); // calls A.foo(long) This line looks incorrect. If there was no foo(long), foo(1L) would call foo(int), then if foo(long) appears, the call will be silently diverted.
Yes, it is incorrect. In reality, it produces an error: Error: function B.foo at B.d(4) conflicts with function A.foo at A.d(3) However, the reason for this is that the literal `1L` is treated as both an int and a long for the purposes of overload resolution, because the compiler can prove that its value will fit into both types. If you change it to a variable: long l; foo(l); ...then A.foo(long) is selected unambiguously, and removing it will cause an error ("no overload matches") rather than calling B.foo(int).
Nov 14 2020
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/13/2020 2:57 PM, 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
 	}
Using: import std; is a bad idea.
Nov 15 2020
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 15.11.20 10:06, Walter Bright wrote:
 On 11/13/2020 2:57 PM, 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
     }
Using:     import std; is a bad idea.
Straw man. Seeing that we're at it: It's also a strange idea to write a program that does not perform any I/O. Also, obviously the AA is overkill, we could just assign 123 to x, which the snippet does three times in a row, but x is never read again! Writing that code is a bad idea; just use void main(){} instead, it will do what you wanted, and without any compilation errors. :o) 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. 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. 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")); } ---
Nov 15 2020
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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.
 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 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.
 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. 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 :-(
 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!
Nov 17 2020
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 18 November 2020 at 01:11:35 UTC, Walter Bright 
wrote:
 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!
Because std.file is imported locally, the call to write calls std.file.write instead of std.stdio.write, and overwrites important_data.txt. If `import std.file` is moved to module scope, the compiler correctly diagnoses that the call is ambiguous.
Nov 17 2020
parent Walter Bright <newshound2 digitalmars.com> writes:
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.
Nov 20 2020
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Nov 17 2020
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 11/17/2020 5:28 PM, Timon Gehr wrote:
 On 18.11.20 02:11, Walter Bright wrote:
 On 11/15/2020 6:24 AM, Timon Gehr wrote:
There is no collision with proper overload resolution. The error is spurious.
Reasonable people can disagree over what "proper" overload resolution is.
 What kinds of special cases have been added, by the way?
I don't remember the details, it was several years ago. And the fact that I don't recall the details suggests it is more complex than necessary. I was aiming for something better than C++ where perhaps 1 in 10,000 C++ programmers can actually explain why one symbol is preferred over another, the other 9,999 just try random things until they get what they want. That I've failed at that is disappointing. But I also know that endlessly tweaking it to find the "proper" solution means endlessly breaking existing code in mysterious ways.
 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!)
And it's been mostly successful at that. But even if it isn't, suddenly breaking existing code doesn't please users.
 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.
That isn't hijacking, it's scoping. Hijacking is when an overload is added to one module that is accidentally a better match than unrelated overloads in another module, when both are in the same scope.
Nov 20 2020
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Nov 20, 2020 at 06:25:59PM -0800, Walter Bright via Digitalmars-d wrote:
 On 11/17/2020 5:28 PM, Timon Gehr wrote:
[...]
 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.
That isn't hijacking, it's scoping. Hijacking is when an overload is added to one module that is accidentally a better match than unrelated overloads in another module, when both are in the same scope.
The problem is that this concept of scoping is very counterintuitive: object.get is (implicitly) imported at module scope. Then std.curl.get is imported in function scope. When `get` is referenced, std.curl.get gets tried first, which is expected. But failing that (because the arguments don't match std.curl.get), one expects the compiler to fallback to the parent scope and try object.get, since, after all, the arguments obviously don't match std.curl.get, the user probably meant to call a different `get` that's in scope. However, the compiler bails out with an error instead. The overall appearance is, std.curl.get has hijacked object.get. It may not *technically* be hijacking, but it certainly looks like it, feels like it, and smells like it. T -- Democracy: The triumph of popularity over principle. -- C.Bond
Nov 21 2020
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 21.11.20 03:25, Walter Bright wrote:

 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.
That isn't hijacking, it's scoping. Hijacking is when an overload is added to one module that is accidentally a better match than unrelated overloads in another module, when both are in the same scope.
You can have three modules where module `A` imports module `B` and `C` and if I add a symbol `foo` in module C, some lookup in `A` changes from `B.foo` to `C.foo`. I'd call that hijacking: You have a relationship between modules `A` and `B` that is intruded upon by some unrelated change in module `C`. (Anyway, my point was actually that this is bad, not that it should be called hijacking. It's even bad for the same reason "overload" hijacking is bad.)
Nov 21 2020
prev sibling next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
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.
Nov 18 2020
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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)
Nov 18 2020
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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.
Nov 18 2020
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Nov 18, 2020 at 10:14:44AM +0000, FeepingCreature via Digitalmars-d
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.
 
 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.
There are several factors at work here. Local imports are good in the sense that you minimize namespace pollution: import the symbol only where you need them, not anywhere else. They are also good for encapsulation: cut and paste your function into another module, and it Just Works. Whereas if it depended on symbols imported at the module level, you now have to copy-n-paste that import into the new module. And we all know what happens to that blob of imports at the top of the file: it just keeps growing and growing, and nobody dares delete it because you don't know what else might depend on it. (Well, theoretically it's easy, just delete them one by one and put back whichever causes a compile error. In practice, though, that's too much work just for deleting a line of code, so nobody does it.) The problems come from *unqualified* local imports, and on this point I agree with you that unqualified local imports are generally a bad idea. We've known of this since a long time ago, actually: void fun(string text) { import std.conv; writeln(text); } void main() { fun("hello world"); } This (used to) output a blank line, because `text` got hijacked by std.conv.text. IIRC somebody subsequently merged a DMD PR to change the lookup rules to fix this case. I thought that PR solved the problem somehow, but as Walter said, it led to one special case after another. And now here we are.
 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 don't agree that `import std` is a mistake. It's a wonderful shortcut for one-off scripts and `echo ... | dmd -` one-liners. Having to explicitly spell out individual Phobos modules would completely kill the latter usage because it becomes too onerous an effort for something that's meant to be a one-time quick hack. In fact, even in larger programs I think `import std` is a net plus, because as somebody has already said, who really cares about that blob of imports at the top of the file spelling out individual Phobos modules? Nobody reads it, and nobody cares except to satisfy the compiler. I can understand if you're importing some 3rd party library and the compiler needs to be told where to find those symbols, but this is the *standard* library, darnit. It's supposed to be standard symbols common across all D programs (more or less). Having to spell that out every single time I use it is just Java-style needless boilerplate. But I do agree that unqualified `import std` as a local import is a bad idea, since it pulls in a whole truckload of symbols that, in all likelihood, will clash with / shadow local symbols. As I said, in general unqualified local imports are bad news, because you don't know what symbols might get pulled in; a local symbol can easily get hijacked by a new symbol added to some 3rd party dependency. Though I have to say, even despite the landmines, local `import std` is useful for inserting temporary one-off debug code into otherwise import-clean code. I often insert `import std; stderr.writefln("debug info");` into strategic points in my code while debugging. It gives you instant access to regular Phobos algorithms and functions without the tedium of painstakingly spelling out every module you might need. And it's temporary and fits on one line, so it's just as easy to delete afterwards without polluting the rest of the code. So love it or hate it, `import std` *does* have its uses. In this particular instance of problems caused by it, my opinion is leaning towards std.curl.get being poorly-named. HTTP GET is a rather narrow and specific function, as opposed to object.get for AA's which are built into the language. If it weren't for that ever-looming spectre of breaking existing code, I'd say rename it to something else. Like curlGet or something. T -- First Rule of History: History doesn't repeat itself -- historians merely repeat each other.
Nov 18 2020
next sibling parent matheus <matheus gmail.com> writes:
On Wednesday, 18 November 2020 at 18:10:58 UTC, H. S. Teoh wrote:
 Local imports are good in the sense that you minimize namespace 
 pollution: import the symbol only where you need them, not 
 anywhere else.
 They are also good for encapsulation: cut and paste your 
 function into another module, and it Just Works.  Whereas if it 
 depended on symbols imported at the module level, you now have 
 to copy-n-paste that import into the new module.  And we all 
 know what happens to that blob of imports at the top of the 
 file: it just keeps growing and growing, and nobody dares 
 delete it because you don't know what else might depend on it.
Yes that's true, but on the other hand you will need to duplicate or repeat yourself with local imports, like: void bar(){ import std.stdio; writeln("bar"); } void foo(){ import std.stdio; writeln("foo"); } void main(string[ ] args){ foo(); bar(); } Wouldn't this generate some pollution as well as your program grows? Matheus.
Nov 18 2020
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Wednesday, 18 November 2020 at 18:10:58 UTC, H. S. Teoh wrote:
 In this particular instance of problems caused by it, my 
 opinion is leaning towards std.curl.get being poorly-named. 
 HTTP GET is a rather narrow and specific function, as opposed 
 to object.get for AA's which are built into the language.  If 
 it weren't for that ever-looming spectre of breaking existing 
 code, I'd say rename it to something else. Like curlGet or 
 something.
std.curl.get is absolutely not at fault here. The whole point of modules (and namespaces in general) is that you can use whatever names you want in them without having to worry about conflicts. And if you need to use two different `get`s in the same scope, D has plenty of tools to help you disambiguate: import curl = std.curl; // ... curl.get(whatever); import std.curl; alias curlGet = std.curl.get; // ... curlGet(whatever); static import std.curl; // ... std.curl.get(whatever);
Nov 18 2020
prev sibling parent reply Kagamin <spam here.lot> writes:
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.
Nov 18 2020
parent FeepingCreature <feepingcreature gmail.com> writes:
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.
Nov 19 2020
prev sibling next sibling parent reply Mathias LANG <geod24 gmail.com> writes:
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.
Nov 18 2020
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Nov 18 2020
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Nov 18 2020
prev sibling parent Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
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
 	}
Can we fix this particular case by adding "public import object;" to std/package.d ?
Nov 20 2020