Thread overview
Scoped Imports for Structs/Classes/Template Constraints
Aug 31, 2015
jmh530
Aug 31, 2015
Enamex
Aug 31, 2015
jmh530
Aug 31, 2015
Enamex
Sep 01, 2015
jmh530
Sep 01, 2015
Enamex
Sep 01, 2015
jmh530
Sep 01, 2015
Enamex
August 31, 2015
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();
}
August 31, 2015
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) { ... }
}
August 31, 2015
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
August 31, 2015
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`
September 01, 2015
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);
	}
}
September 01, 2015
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.
September 01, 2015
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?
September 01, 2015
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?