www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - filtered imports

reply Jonathan Marler <johnnymarler gmail.com> writes:
"Selective imports" limit the symbols imported from a module by 
providing a list of all the symbols to include:

import std.stdio : writeln, writefln;

The complement of this would be a "Filtered import", meaning, 
import all the symbols except the ones in the provided list. I 
imagine the syntax would look something like:

import std.stdio ~ writeln, writefln;


To provide a use case for this, say you have a module that uses a 
fair amount of symbols from `std.file` but you want to make sure 
that all calls to `chdir` are logged.  Using a filtered import 
would allow you to exclude `chdir` from being available globally 
so you could create a wrapper that all code is forced to go 
through.

import std.stdio;
import std.file ~ chdir;

void chdir(R)(R path)
{
     writefln("chdir '%s'", path);
     from!"std.file".chdir(path);
}

It's an interesting variation on D's current repertoire of import 
semantics. Possibly worth consideration as an addition to the 
language.

Grammar Changes:
-----------------------------------------------------
ImportBindings:                    (existing rule)
     Import : ImportBindList        (existing rule)
     Import ~ ImportExcludeList     (new rule)

ImportExcludeList:                 (new rule)
     Identifier, ImportExcludeList  (new rule)
-----------------------------------------------------
Sep 13 2018
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 13/09/2018 11:54 PM, Jonathan Marler wrote:
 "Selective imports" limit the symbols imported from a module by 
 providing a list of all the symbols to include:
 
 import std.stdio : writeln, writefln;
 
 The complement of this would be a "Filtered import", meaning, import all 
 the symbols except the ones in the provided list. I imagine the syntax 
 would look something like:
 
 import std.stdio ~ writeln, writefln;
 
 
 To provide a use case for this, say you have a module that uses a fair 
 amount of symbols from `std.file` but you want to make sure that all 
 calls to `chdir` are logged.  Using a filtered import would allow you to 
 exclude `chdir` from being available globally so you could create a 
 wrapper that all code is forced to go through.
 
 import std.stdio;
 import std.file ~ chdir;
 
 void chdir(R)(R path)
 {
      writefln("chdir '%s'", path);
      from!"std.file".chdir(path);
 }
 
 It's an interesting variation on D's current repertoire of import 
 semantics. Possibly worth consideration as an addition to the language.
 
 Grammar Changes:
 -----------------------------------------------------
 ImportBindings:                    (existing rule)
      Import : ImportBindList        (existing rule)
      Import ~ ImportExcludeList     (new rule)
 
 ImportExcludeList:                 (new rule)
      Identifier, ImportExcludeList  (new rule)
 -----------------------------------------------------
import std.stdio; import std.file; void chdir(R)(R path) { writeln("changing dir to ", path); std.file.chdir(path); } void main() { chdir("/tmp"); }
Sep 13 2018
next sibling parent Neia Neutuladh <neia ikeran.org> writes:
On Thursday, 13 September 2018 at 11:58:40 UTC, rikki cattermole 
wrote:
 import std.stdio;
 import std.file;

 void chdir(R)(R path) {
 	writeln("changing dir to ", path);
     std.file.chdir(path);
 }
And if you don't want to write long qualified import names, just rename it: import sf = std.file; sf.chdir("foo"); chdir("foo");
Sep 13 2018
prev sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 13 September 2018 at 11:58:40 UTC, rikki cattermole 
wrote:
 On 13/09/2018 11:54 PM, Jonathan Marler wrote:
 "Selective imports" limit the symbols imported from a module 
 by providing a list of all the symbols to include:
 
 import std.stdio : writeln, writefln;
 
 The complement of this would be a "Filtered import", meaning, 
 import all the symbols except the ones in the provided list. I 
 imagine the syntax would look something like:
 
 import std.stdio ~ writeln, writefln;
 
 
 To provide a use case for this, say you have a module that 
 uses a fair amount of symbols from `std.file` but you want to 
 make sure that all calls to `chdir` are logged.  Using a 
 filtered import would allow you to exclude `chdir` from being 
 available globally so you could create a wrapper that all code 
 is forced to go through.
 
 import std.stdio;
 import std.file ~ chdir;
 
 void chdir(R)(R path)
 {
      writefln("chdir '%s'", path);
      from!"std.file".chdir(path);
 }
 
 It's an interesting variation on D's current repertoire of 
 import semantics. Possibly worth consideration as an addition 
 to the language.
 
 Grammar Changes:
 -----------------------------------------------------
 ImportBindings:                    (existing rule)
      Import : ImportBindList        (existing rule)
      Import ~ ImportExcludeList     (new rule)
 
 ImportExcludeList:                 (new rule)
      Identifier, ImportExcludeList  (new rule)
 -----------------------------------------------------
import std.stdio; import std.file; void chdir(R)(R path) { writeln("changing dir to ", path); std.file.chdir(path); } void main() { chdir("/tmp"); }
Ah, I see. Poor example on my part. I didn't realize that local symbols ALWAYS take precedence over imported ones. I thought that the compiler would "fall back" to an imported symbol if the local one didn't work, but it appears that's not the case. Some more thoughts. Selective imports and filtered imports are complements to each other, you can still have all the functionality you want with just one of them, but one will usually fit better in each case. Namely, if you want a small number of symbols from a module, selective imports are the way to go, but if you want to include all except a small number of symbols from a module, then filtered imports would be nice. So a use case would only work better with filtered imports if it was a scenario where we want to include all except a few symbols from an imported module. Given that, the next question is, in what cases do we want to prevent symbols from being imported from a module? The immediate example is to resolve symbol conflicts. // assume both modules provide the symbol baz import foo; import bar; baz(); // symbol conflict // change `import foo;` to `import foo ~ baz;` And to reiterate, we could also resolve this conflict with selective imports (or even static or named imports in this case) so you would only consider this useful in cases where we are using a good amount of symbols from foo as to make it cumbersome to have to selectively import all the symbols or qualify them all in source, i.e. import foo : a, b, c, d, e, f, g, h, i, j, k, ...; // vs import foo ~ baz; Anyway, those are my thoughts on it. Again I think it's something to consider. I'm not sure if it has enough uses to justify an addition to the language (that bar is pretty high now-a-days). So it's up to us D programmers to think about whether these semantics would work well in our own projects.
Sep 13 2018
parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Thursday, 13 September 2018 at 16:23:21 UTC, Jonathan Marler 
wrote:
 The immediate example is to resolve symbol conflicts.
I've ran into this a few times: import std.stdio; import std.file; void main(string[] args) { auto text = readText(args[1]); write("The contents of the file is: ", text); } However, it is solved with an alias: alias write = std.stdio.write; (or using selective imports or fully-qualified identifiers for either module of course).
Sep 13 2018
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 13 September 2018 at 17:54:03 UTC, Vladimir 
Panteleev wrote:
 [snip]

 However, it is solved with an alias:

 alias write = std.stdio.write;

 (or using selective imports or fully-qualified identifiers for 
 either module of course).
That's a nice trick!
Sep 13 2018
prev sibling parent Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 13 September 2018 at 17:54:03 UTC, Vladimir 
Panteleev wrote:
 On Thursday, 13 September 2018 at 16:23:21 UTC, Jonathan Marler 
 wrote:
 The immediate example is to resolve symbol conflicts.
I've ran into this a few times: import std.stdio; import std.file; void main(string[] args) { auto text = readText(args[1]); write("The contents of the file is: ", text); } However, it is solved with an alias: alias write = std.stdio.write; (or using selective imports or fully-qualified identifiers for either module of course).
That's pretty slick. I guess I'm all out of use cases then. I did learn a new trick though :)
Sep 13 2018