www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Scoped Imports for Structs/Classes/Template Constraints

reply "jmh530" <john.michael.hall gmail.com> writes:
I've been thinking about scoped imports. While it seems like you 
can use scoped imports a lot of the time, it seems like there are 
some potential cases where you can't. Most notably if a function 
input is a struct or class, then I can't figure out a way to use 
a scoped import. This also applies to template constraints I 
think.

I put some code below to illustrate what I mean. The local import 
has to apply to the entire app module in order for foo to work. 
It can't only apply to the foo function the way that the 
std.stdio import can. By contrast, bar uses a scoped import of S 
(which works even if the first import is removed), but it means 
that you can't pass s in.

Is there any way to use a scoped import of S with foo that I may 
have overlooked?

module local;

struct S { int a = 2; }

module app;

import local : S;

void foo(S s)
{
	import std.stdio : writeln;
	writeln(s.a);
}

void bar()
{
	import std.stdio : writeln;
	import local : S;
	S s;
	writeln(s.a);
}

void main()
{
	S s;
	foo(s);
	bar();
}
Aug 31 2015
parent reply "Enamex" <enamex+d outlook.com> writes:
On Monday, 31 August 2015 at 19:10:45 UTC, jmh530 wrote:
 module local;

 struct S { int a = 2; }

 module app;

 import local : S;

 void foo(S s)
 {
 	import std.stdio : writeln;
 	writeln(s.a);
 }

 void bar()
 {
 	import std.stdio : writeln;
 	import local : S;
 	S s;
 	writeln(s.a);
 }

 void main()
 {
 	S s;
 	foo(s);
 	bar();
 }
You can't use it with foo(S) because the type is used /outside/ the scope of the function, in its head. You have to qualify it ( void foo(local.S s) ) or import the type as: { import local: S; void foo(S s) { ... } }
Aug 31 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
On Monday, 31 August 2015 at 20:54:53 UTC, Enamex wrote:
 You can't use it with foo(S) because the type is used /outside/ 
 the scope of the function, in its head. You have to qualify it 
 ( void foo(local.S s) )  or import the type as:

 { import local: S;
     void foo(S s) { ... }
 }
I figured the scoping was the issue. Just curious if there was a trick around it. The only thing I could think of was just using more sub-modules to break up the code by imports. The downside is that it might lead to longer compile times. I'm not sure about how the first local.S resolves things. I had been using a selective import for S before. To get the local.S to compile, I have to change the import to a normal one. I'm concerned about the case where local contains more than just S. Suppose I import app : foo; in another module, wouldn't this import all of local? Alternately, suppose I create some function baz that does not use S in any way, then if I import app : baz, then I will inadvertently import local, correct? The second thing does not compile for me (I had tried that already...). I get app.d(3): Error: declaration expected, not '{' app.d(10): Error: unrecognized declaration
Aug 31 2015
parent reply "Enamex" <enamex+d outlook.com> writes:
On Monday, 31 August 2015 at 21:22:07 UTC, jmh530 wrote:
 I'm not sure about how the first local.S resolves things. I had 
 been using a selective import for S before. To get the local.S 
 to compile, I have to change the import to a normal one. I'm 
 concerned about the case where local contains more than just S. 
 Suppose I import app : foo; in another module, wouldn't this 
 import all of local? Alternately, suppose I create some 
 function baz that does not use S in any way, then if I import 
 app : baz, then I will inadvertently import local, correct?

 The second thing does not compile for me (I had tried that 
 already...). I get

 app.d(3): Error: declaration expected, not '{'
 app.d(10): Error: unrecognized declaration
I'm not sure whether a naked 'local.S' should work; sometimes it does and sometimes it doesn't. But an `static import local;` then `voidfoo(local.S)` definitely works. `static import` forces the imported symbols to be fully qualified on use. It basically only allows the module to recognize the existence of modules outside its package. Doing `import app: foo;` or even just `import app;` would never import `module app;`'s imports unless those imports are public. `module foo; public static import local; import bazoo;` `module bar; void fun() { local.S s; } // works void fun2() { bazoo.B b; } // error`
Aug 31 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
On Monday, 31 August 2015 at 23:36:25 UTC, Enamex wrote:
 I'm not sure whether a naked 'local.S' should work; sometimes 
 it does and sometimes it doesn't. But an `static import local;` 
 then `voidfoo(local.S)` definitely works.

 `static import` forces the imported symbols to be fully 
 qualified on use. It basically only allows the module to 
 recognize the existence of modules outside its package.

 Doing `import app: foo;` or even just `import app;` would never 
 import `module app;`'s imports unless those imports are public.

 `module foo; public static import local; import bazoo;`
 `module bar; void fun() { local.S s; } // works
 void fun2() { bazoo.B b; } // error`
I'm following some of your post, but not quite all of it (particularly the part at the end, and I also think static imports can't be selective). Anyway, I was thinking about something like below as one possible alternative struct T { import local : S; void foo(S s) { import std.stdio : writeln; writeln(s.a); } }
Sep 01 2015
parent reply "Enamex" <enamex+d outlook.com> writes:
On Tuesday, 1 September 2015 at 15:50:40 UTC, jmh530 wrote:
 I'm following some of your post, but not quite all of it 
 (particularly the part at the end, and I also think static 
 imports can't be selective). Anyway, I was thinking about 
 something like below as one possible alternative

 struct T
 {
 	import local : S;
 	void foo(S s)
 	{
 		import std.stdio : writeln;
 		writeln(s.a);
 	}
 }
Bad idea to wrap it in a struct like that. An alternative to static import would be to `import loc = local;` which introduces `loc` but doesn't go further; everything in `local` has to be qualified as `loc.*`. They aren't selective, yeah. But the rationale is good: There's not supposed to be any way to import modules with the same path so static importing means it's entirely and always unambiguous. The idea was to static import 'local' (to introduce the 'local' name itself into scope), which lets you use its contents only fully-qualified and, basically solves your issue. You could still `import local;` inside the function to use its contents unqualified.
Sep 01 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
On Tuesday, 1 September 2015 at 19:48:02 UTC, Enamex wrote:
 They aren't selective, yeah. But the rationale is good: There's 
 not supposed to be any way to import modules with the same path 
 so static importing means it's entirely and always unambiguous.
I understand that a static import is always unambiguous. That's really the only thing the documentation says about static imports. I wonder if there are any other difference. Consider these three different ways to import std.stdio import std.stdio; import std.stdio : writeln; static import std.stdio; and suppose writeln is the only function in std.stdio the program is using. In each case, the size of the exe is exactly the same. So presumably, the compiler is smart enough to see that std.stdio.chunks, for instance, isn't used and not to include in the final program. However, there might be a difference in compile times (though I don't exactly know how to measure that). My understanding is that import std.stdio; leads to all of std.stdio being compiled, but import std.stdio : writeln leads to only writeln being compiled (and anything it depends on). There are also faster lookups during compilation, or something, I think. Given that the documentation only indicates the difference between import std.stdio and static import std.stdio is the ambiguous-ness that you point out, I would expect that the time to compile would be the same as import std.stdio. Is this correct?
Sep 01 2015
parent "Enamex" <enamex+d outlook.com> writes:
On Tuesday, 1 September 2015 at 21:17:10 UTC, jmh530 wrote:
 Consider these three different ways to import std.stdio
 import std.stdio;
 import std.stdio : writeln;
 static import std.stdio;
 and suppose writeln is the only function in std.stdio the 
 program is using.

 In each case, the size of the exe is exactly the same. So 
 presumably, the compiler is smart enough to see that 
 std.stdio.chunks, for instance, isn't used and not to include 
 in the final program. However, there might be a difference in 
 compile times (though I don't exactly know how to measure that).

 My understanding is that import std.stdio; leads to all of 
 std.stdio being compiled, but import std.stdio : writeln leads 
 to only writeln being compiled (and anything it depends on). 
 There are also faster lookups during compilation, or something, 
 I think. Given that the documentation only indicates the 
 difference between import std.stdio and static import std.stdio 
 is the ambiguous-ness that you point out, I would expect that 
 the time to compile would be the same as import std.stdio. Is 
 this correct?
Oh no. Importing in D is a symbol table import. If you're using a module and it's only available in source-format to the compiler, then it has to compile the whole module even if you're using just one function (because internally the whole module is likely to be very tightly coupled, and usually should be, if it's under one name). import std.stdio; // makes available for calling all symbols in std.stdio; compiles module if not pre-compiled and if at least one symbol is used (not sure about the last point) import std.stdio : writeln; // makes available for calling only 'writeln()' from std.stdio; compiles module if not pre-compiled static import std.stdio; // makes available for calling all symbols in std.stdio but only when fully qualified; compiles module if not pre-compiled As I understand it, the only difference between cases with different numbers of called symbols from a module is apparent during linking, not compilation. Anyone can shed light on this?
Sep 01 2015