www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Inline imports redivivus

reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
https://forum.dlang.org/post/sdehom$10fi$1 digitalmars.com

On Friday, 23 July 2021 at 13:55:34 UTC, Andrei Alexandrescu 
wrote:

 If it works well in practice, a future language proposal could 
 take
 `_import!"std.datetime".SysTime` to the simpler and better
 `import(std.datetime).SysTime`.
Yes, please!
Mar 07 2022
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 3/7/22 02:27, Per Nordlöw wrote:
 https://forum.dlang.org/post/sdehom$10fi$1 digitalmars.com

 On Friday, 23 July 2021 at 13:55:34 UTC, Andrei Alexandrescu wrote:

 If it works well in practice, a future language proposal could take
 `_import!"std.datetime".SysTime` to the simpler and better
 `import(std.datetime).SysTime`.
Yes, please!
This seems to be yet another example of the need for template string arguments typed as symbols in the source code. - Flag!"foo" could become Flag(foo) (or more consistently Flag!foo). - bitfields could become bitfields!(int, x, 8) instead of bitfields!(int, "x", 8)[*] Andrei mentioned a couple of times that this is what was discussed as "new alias" in the past where the template allows the use of an "unknown" (or "new") symbol at call site. I see a strong similarity here to the existing opDispatch() that makes compile-time strings from "unknown" symbols at call site. Ali [*] I think it would be interesting to come up with a language solution that would e.g. enable combining each three consecutive arguments of bitfields. It is a code smell that everybody knows that the three separate arguments go together but we don't use a language construct for that. We already have the following method in the current language but it is obviously redundant and perhaps that's why the current design of bitfields does not use it: bitfields!(Bit(int, x, 8), // Better but redundant Bit(int, y, 8)); A solution that would allow the following would be interesting: bitfields!(int x : 8, int y : 8); So, we somehow specify at template definition that two (or three?) parameters (e.g. int and x) can be specified without a comma and perhaps anything after ':' are UDAs of that parameter?
Mar 07 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 7 March 2022 at 18:45:54 UTC, Ali Çehreli wrote:
 A solution that would allow the following would be interesting:

   bitfields!(int x : 8,
              int y : 8);

 So, we somehow specify at template definition that two (or 
 three?) parameters (e.g. int and x) can be specified without a 
 comma and perhaps anything after ':' are UDAs of that parameter?
There's always strings: bitfields!("int x : 8", "int y : 8");
Mar 08 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 3/8/22 14:28, Paul Backus wrote:

 There's always strings:
And they are the eyesore! :)
      bitfields!("int x : 8",
                 "int y : 8");
But you give me the idea that if we allow "this template parameter is a string" and don't require strings at the use site, then it would work for the above case as well. Basically, the string is contains my domain specific language as you show and I can do anything I want with it. Brain storming with a 'new string' syntax: template bitfields(new string args...) { // ... } bitfields!(int x : 8, int y : 8); And the template instance sees two strings: "int x : 8" and "int y : 8" and has fun parsing them at compile time. Again, this feels natural to me because it is related to opDispatch. Of course there are syntax issues: Nothing that takes part in a parameter list can be a part of the string. I think they are just ',' to allow multiple 'new string' parameters and ')' to allow closing the parameter list. When needed, they could be written with backslashes as \, and \). Wow! I've just written a DIP! :P Ali
Mar 08 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Mar 08, 2022 at 03:25:49PM -0800, Ali Çehreli via Digitalmars-d wrote:
 On 3/8/22 14:28, Paul Backus wrote:
 
 There's always strings:
And they are the eyesore! :)
      bitfields!("int x : 8",
                 "int y : 8");
But you give me the idea that if we allow "this template parameter is a string" and don't require strings at the use site, then it would work for the above case as well. Basically, the string is contains my domain specific language as you show and I can do anything I want with it.
[...] Token strings are your friend: bitfields!q{ int x:8; int y:8; }; Or, if you like struct-like syntax: bitfields!q{ int x:8; int y:8; }; T -- Questions are the beginning of intelligence, but the fear of God is the beginning of wisdom.
Mar 08 2022
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 3/8/22 17:15, H. S. Teoh wrote:

 Token strings are your friend:
I know but they are still strings. Flag!"foo" a; // Meh Flag!`foo` b; // Meh Flag!q{foo} c; // No comment... D'oh! :) Flag!foo d; // Unavailable This is a minor thing only Ali dislikes... :/ Ali
Mar 08 2022
prev sibling parent reply Zach Tollen <zach mystic.yeah> writes:
On Monday, 7 March 2022 at 10:27:08 UTC, Per Nordlöw wrote:
 https://forum.dlang.org/post/sdehom$10fi$1 digitalmars.com

 On Friday, 23 July 2021 at 13:55:34 UTC, Andrei Alexandrescu 
 wrote:

 If it works well in practice, a future language proposal could 
 take
 `_import!"std.datetime".SysTime` to the simpler and better
 `import(std.datetime).SysTime`.
Yes, please!
The reason I wanted to revive this redivivus — so to say — is because I believe the following syntax is near-optimal for the task at hand: `import.std.datetime:SysTime` From my perspective there is no need to rely on advanced template features since inline imports are so basic that they should be adopted as a language feature instead. The above syntax is excellent because: - It is grammatically unambiguous - It requires no parentheses or other enclosing marks - It resembles selective imports That is exactly what it is: a use-once, selective import. You use it as a drop-in replacement for the identifier you want to import. In reviewing the historical proposals even as far back as DIP1005 [1], it seems that the syntax above simply wasn't stumbled upon. Therefore I wanted it to be evaluated. "Destroy!" To give a little more food for thought, in addition to the above syntax, I wanted to introduce a stunning syntax sugar, which uses `$` instead of `import .` as a special token to activate the sequence: `$std.datetime:SysTime` This is the tersest fully qualified inline import possible. I suppose it is strange on the eyes. But don't implement it unless you want people to use it, because people can hardly resist a concise syntax like this! Anyway, those are my proposals. Here are some previous proposals and why I have problems with them: ```d imported!"std.datetime".SysTime // - already implemented in object.d [2] // - best you can do with existing language // - can't use keyword `import` // - requires `""` and `!` import(std.datetime).SysTime // parens unnecessary (import std.datetime).SysTime // ditto std.datetime::SysTime // grammatically ambiguous (module name unclear) ::std.datetime:SysTime // if we put the `::` first it becomes viable // it's a different version of the `$` ``` [1] https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1005.md#syntactic-alternatives [2] https://github.com/dlang/druntime/pull/1756/files
Mar 10 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 3/10/22 22:48, Zach Tollen wrote:

 ::std.datetime:SysTime          // if we put the `::` first it becomes
 viable
                                  // it's a different version of the `$`
Perhaps a second meaning for ..? And is : really necessary? So, would the following work? ..std.datetime.Systime Now it seems to have a different meaning from "importing" though. In this case it feels like we are "using" Systime from that module. And the rationale for .. can be: - Single dot already means "this module". - Two dots may mean "outside of this module". Going off topic and without knowing whether this topic has already been brought up, should inline imports automatically be made available to the callers of functions? Arbitrarily using the $ syntax: module my.module; void foo($bar:SomeSymbol a) { } Since 'foo' is public to the users of my.module, should we expect the users be able to call 'foo' without importing SomeSymbol separately themselves? And the next question may be what if SomeSymbol is accessible to my.module but not to my users (e.g. if it's 'package'). So, if it should automatically be imported for the users, should SomeSymbol automatically be raised to 'public' status for the users of 'foo'? Ali
Mar 11 2022
next sibling parent Zach Tollen <zach mystic.yeah> writes:
Thanks for your reply!

On Friday, 11 March 2022 at 13:01:43 UTC, Ali Çehreli wrote:
 On 3/10/22 22:48, Zach Tollen wrote:
 ::std.datetime:SysTime          // if we put the `::` first
it becomes
 viable
                                  // it's a different version
of the `$`

 Perhaps a second meaning for ..? And is : really necessary? So, 
 would the following work?

   ..std.datetime.Systime
If you used `..` here you would always have to add whitespace in an ordinary call chain, to avoid ambiguous lexing, so it's not good: ```d { x = normal.call.chain; x = dotdot.call. ..modname:chain; // space necessary to avoid lexing as `...` } ``` As far as not wanting to use `:` the issue is also ambiguity. In DIP1005, written a few years ago by Andrei Alexandrescu, this exact question was addressed [1]. Quote: ```d void process(import.std.stdio.File input); struct Buffered(Range) if (import.std.range.isInputRange!Range) { ... } ``` "Such an option has an ambiguity problem shown by Timon Gehr: is import.std.range.isInputRange looking up symbol isInputRange in module/package std.range, or the symbol range.isInputRange (e.g. a struct member) in module/package std?"
 - Single dot already means "this module".
 - Two dots may mean "outside of this module".
If we can find a starting token (other than `$`) which allows the construct to seem familiar and which has no other problems, I'm all for it. `::` has been suggested. I believe also `:` would work: ```d auto x = :std.datetime:Systime; ``` Technically, `:` would not cause grammatical ambiguity. But I don't think it's visually distinct enough, and the error messages you get if you don't type it correctly would be confused with the compiler's other error messages for `:` syntaxes. At minimum, `$` as in `$fully.qualified:symbol` announces its presence boldly, as if to challenge the language designer to come up with a better use of the `$` token. :-)
 Going off topic and without knowing whether this topic has 
 already been brought up, should inline imports automatically be 
 made available to the callers of functions? Arbitrarily using 
 the $ syntax:

 module my.module;

 void foo($bar:SomeSymbol a) {
 }

 Since 'foo' is public to the users of my.module, should we 
 expect the users be able to call 'foo' without importing 
 SomeSymbol separately themselves?
I would say no. As a one-use import, it doesn't spread its import anywhere but to itself. But the caller can just call using `foo($bar:SomeSymbol(value))` if they don't feel like importing the module either.
 And the next question may be what if SomeSymbol is accessible 
 to my.module but not to my users (e.g. if it's 'package'). So, 
 if it should automatically be imported for the users, should 
 SomeSymbol automatically be raised to 'public' status for the 
 users of 'foo'?
Again, no. If a function parameter requires a privately defined structure, then that function is not meant to be called by things which don't have access to that structure. Inline imports are semantically no different from symbols which had their modules imported separately. I'm pretty sure issues of privacy won't be affected by this. - Zach [1] https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1005.md#syntactic-alternatives
Mar 11 2022
prev sibling parent reply Zach Tollen <zach mystic.yeah> writes:
On Friday, 11 March 2022 at 13:01:43 UTC, Ali Çehreli wrote:
 Perhaps a second meaning for ..? And is : really necessary?
The character `|` might work too. It looks better if we close it with the same character. ```d x = |std.datetime|SysTime; ``` I actually like this one a lot. There is some potential lexical ambiguity with `|` and `||` as binary operators. However, those operators are typically separated with whitespace in ordinary code, so I don't think it will ever be a problem. (The second `|` in `|std.datetime|SysTime` can be parsed as a part of `|modname|symbol`, so apart from typos, there's no syntactical problem.) The worst you would suffer would be the visual confusion of something like this: ```d if (|std.something|condition(sdfd) || |some.other.inlinemodule|Test || |othermod.imp|mycondition) { myFlags = |std.something|Flagset | |std.otherthing|Flags; } ``` This would be the extreme case I think. Syntax highlighting could help. I think this is better than `$std.datetime:SysTime`.
Mar 12 2022
parent reply Daniel N <no public.email> writes:
On Saturday, 12 March 2022 at 19:13:33 UTC, Zach Tollen wrote:
 On Friday, 11 March 2022 at 13:01:43 UTC, Ali Çehreli wrote:
 Perhaps a second meaning for ..? And is : really necessary?
The character `|` might work too. It looks better if we close it with the same character. ```d x = |std.datetime|SysTime; ``` I actually like this one a lot. There is some potential lexical ambiguity with `|` and `||` as binary operators. However, those operators are typically separated with whitespace in ordinary code, so I don't think it will ever be a problem. (The second `|` in `|std.datetime|SysTime` can be parsed as a part of `|modname|symbol`, so apart from typos, there's no syntactical problem.) The worst you would suffer would be the visual confusion of something like this: ```d if (|std.something|condition(sdfd) || |some.other.inlinemodule|Test || |othermod.imp|mycondition) { myFlags = |std.something|Flagset | |std.otherthing|Flags; } ``` This would be the extreme case I think. Syntax highlighting could help. I think this is better than `$std.datetime:SysTime`.
Out of your new alternatives, I like this one the best. ```d x = :std.datetime:SysTime; ``` 1) It is symmetric same as || 2) : is already used in import x:y 3) : is not used in expressions unlike | which is quite common. 4) We can keep $ for some future codegen features.
Mar 12 2022
parent reply Zach Tollen <zach mystic.yeah> writes:
On Saturday, 12 March 2022 at 19:33:16 UTC, Daniel N wrote:
 Out of your new alternatives, I like this one the best.
 ```d
 x = :std.datetime:SysTime;
 ```

 1) It is symmetric same as ||
 2) : is already used in import x:y
 3) : is not used in expressions unlike | which is quite common.
 4) We can keep $ for some future codegen features.
Thanks. Those are good points. I think I even agree with you. Regarding (3), there is an exception in the case of the conditional operator (`? :`), but I don't think it's a huge problem. The worst visual confusion here might be something like: ```d { auto x = :mymod.question:ask ? :another.modname:asdf ? :moremods:answer1 : :finalmod.three:okay : :mymod.answer:two } ``` To do a general comparison of the versions, let's take this code snippet from [phobos](https://github.com/dlang/phobos/blob/16cb085b584f100fa677e2e64ff6b6dbb4921ad1/std/algorithm/comparison.d#L88): The way it is now: ```d if (Values.length != 0) { foreach (uint i, ref v; values) { import std.functional : binaryFun; if (binaryFun!pred(value, v)) return i + 1; } return 0; } ``` Ugh. Not my favorite. Now let's look at the [pull request](https://github.com/dlang/druntime/pull/1756/files) which was already merged, which allows you to write the following: ```d if (Values.length != 0) { foreach (uint i, ref v; values) { if (imported!"std.functional".binaryFun!pred(value, v)) return i + 1; } return 0; } ``` I'd say this represents 70% of the total aesthetic improvement between the versions. Just being able to inline imports makes most of the difference here — not bad for a technique with no language support! (We're not talking about compilation speed, although direct language support would allow the compiler to bypass the normal lookup process and go straight to module where the import is located, so it would speed up.) Using my language-supported long form `import` syntax, the result is: ```d if (Values.length != 0) { foreach (uint i, ref v; values) { if (import.std.functional:binaryFun!pred(value, v)) return i + 1; } return 0; } ``` I'll give this 5% of the total aesthetic improvement, from the previous version to this. Finally, with `:modname:symbol`: ```d if (Values.length != 0) { foreach (uint i, ref v; values) { if (:std.functional:binaryFun!pred(value, v)) return i + 1; } return 0; } ``` I give this the remaining 25% of the total improvement. I'm curious what other people think.
Mar 12 2022
parent reply Dom DiSc <dominikus scherkl.de> writes:
On Saturday, 12 March 2022 at 23:57:09 UTC, Zach Tollen wrote:
 I'm curious what other people think.
Locality can be overdone. I think this would suffice: ```d if (Values.length != 0) { import std.functional : binaryFun; foreach (uint i, ref v; values) if (binaryFun!pred(value, v)) return i + 1; return 0; } ``` So any new feature is here totally superfluous IMHO.
Mar 13 2022
next sibling parent reply Dom DiSc <dominikus scherkl.de> writes:
On Sunday, 13 March 2022 at 08:23:52 UTC, Dom DiSc wrote:
 So any new feature is here totally superfluous IMHO.
And btw. foreach doesn't even introduce a new scope, so you can't get your import more local than in my version, no matter what new syntax you invent.
Mar 13 2022
next sibling parent zjh <fqbqrr 163.com> writes:
On Sunday, 13 March 2022 at 08:37:15 UTC, Dom DiSc wrote:

 So any new feature is here totally superfluous IMHO.
Agree, There is no need to be too `local` at all. You only need to put the same `imported file` function into one file.
Mar 13 2022
prev sibling next sibling parent Adam D Ruppe <destructionator gmail.com> writes:
On Sunday, 13 March 2022 at 08:37:15 UTC, Dom DiSc wrote:
 And btw. foreach doesn't even introduce a new scope
Yes it does.
Mar 13 2022
prev sibling parent reply Tejas <notrealemail gmail.com> writes:
On Sunday, 13 March 2022 at 08:37:15 UTC, Dom DiSc wrote:
 On Sunday, 13 March 2022 at 08:23:52 UTC, Dom DiSc wrote:
 So any new feature is here totally superfluous IMHO.
And btw. foreach doesn't even introduce a new scope, so you can't get your import more local than in my version, no matter what new syntax you invent.
`foreach` **does** introduce a new scope I believe it is `static foreach` that **doesn't** introduce a new scope(hence why you see `static foreach(){{ /*code/* }}` style code sometimes
Mar 13 2022
parent reply bauss <jj_1337 live.dk> writes:
On Sunday, 13 March 2022 at 12:47:05 UTC, Tejas wrote:
 On Sunday, 13 March 2022 at 08:37:15 UTC, Dom DiSc wrote:
 On Sunday, 13 March 2022 at 08:23:52 UTC, Dom DiSc wrote:
 So any new feature is here totally superfluous IMHO.
And btw. foreach doesn't even introduce a new scope, so you can't get your import more local than in my version, no matter what new syntax you invent.
`foreach` **does** introduce a new scope I believe it is `static foreach` that **doesn't** introduce a new scope(hence why you see `static foreach(){{ /*code/* }}` style code sometimes
Would be nice if we could attribute static foreach like this instead: ```d scope static foreach (...) { ... } or static foreach (...) scope { ... } ``` Would've been much cleaner.
Mar 15 2022
parent reply Andrea Fontana <nospam example.com> writes:
On Tuesday, 15 March 2022 at 10:25:54 UTC, bauss wrote:
 ```d
 scope static foreach (...) { ... }

 or

 static foreach (...) scope { ... }
 ```

 Would've been much cleaner.
```d { static foreach(...) { } } ```
Mar 15 2022
parent bauss <jj_1337 live.dk> writes:
On Tuesday, 15 March 2022 at 13:53:51 UTC, Andrea Fontana wrote:
 On Tuesday, 15 March 2022 at 10:25:54 UTC, bauss wrote:
 ```d
 scope static foreach (...) { ... }

 or

 static foreach (...) scope { ... }
 ```

 Would've been much cleaner.
```d { static foreach(...) { } } ```
The whole point is to avoid the extra brackets and/or the extra unnecessary indentation.
Mar 15 2022
prev sibling parent Zach Tollen <zach mystic.yeah> writes:
On Sunday, 13 March 2022 at 08:23:52 UTC, Dom DiSc wrote:
 Locality can be overdone. I think this would suffice:

 ```d
 if (Values.length != 0)
 {
     import std.functional : binaryFun;
     foreach (uint i, ref v; values)
         if (binaryFun!pred(value, v)) return i + 1;
     return 0;
 }
 ```

 So any new feature is here totally superfluous IMHO.
There are three considerations here: - Functionality - What you can do - Performance - How fast you can do it - Readability - How easy it is to see what you're doing Your argument about it being superfluous addresses Functionality and Performance, but not Readability. And the feature is not entirely superfluous with regards to Functionality and Performance either. But I'll address those later. Regarding performance in this particular case, I agree with you. When you import a symbol as locally as is done in the example above, the compiler find its definition quickly enough. The only slow contender in this case is `imported` from object.d: ```d if (imported!"std.functional".binaryFun!pred(value, v)) return i + 1; ``` Regarding functionality, the functionality between having a separate import statement and the inline one is the similar in ordinary code. Both the statement and the inline bring in the symbol and then use it once. It should be pointed out that if you wanted to use the symbol again in the same scope, the separate import statement is superior. This is a deficiency of the proposed single-use imports that I will address below. But we have to consider readability. Ideally, code is written in a way which allows a programmer to see clearly what is important, without having to be distracted by what is not important. Unimportant code which takes too prominent a place constitutes an undesirable distraction — in the recent thread about Exceptions vs. other Error Handling, the chief fear with Error Codes is that they make unimportant code too prominent. Because symbols must be imported to be used, programmers have gotten used to seeing what is imported blasted like a trumpet at the top of their scope or module. ```d { // Look at me! I must be important! import std.functional : binaryFun; ... } ``` But the actual importance of any given imported symbol depends greatly on the specific symbol and the context. Certainly, the readability of some code is not diminished by having notable symbols be prominently announced before they are used. But just as often, the imported functionality is so ordinary that it is nothing but a distraction having to see it promoted so blatantly. Therefore, being able to inline imports, and make them as innocuous as possible, will enable the programmer to indicate how significant each symbol actually is, instead of forcing them all into the limelight. Local imports, even though they can force unimportant symbols into the limelight, have been widely adopted in D for perhaps two reasons — performance and readability. Performance, because of shortened lookup times. And readability, because you can more easily find the source of a specified symbol the closer its definition is to its usage. But local imports have also come at a cost to readability, in the sense that they are a visual distraction. We can say, at least in the case of module level imports, that you can just use their symbols without much [fanfare](https://en.wikipedia.org/wiki/Fanfare). ------------ We have to address functionality. It was argued above that inline imports as a feature are "totally superfluous." Even apart from readability, that is not true. Particularly, with regards to template constraints, they are necessary in order to be able to use local imports at all. This was written about with a level of rigor suited to a serious academic paper in Andrei Alexandrescu's [DIP1005](https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1005.md). That proposal suggests the following syntax to allow inline imports at the template level: ```d with (import std.meta : allSatisfy) with (import std.range : ElementEncodingType) with (import std.traits : hasIndirections, isDynamicArray, isIntegral) auto uninitializedArray(T, I...)(I sizes) nothrow system if (isDynamicArray!T && allSatisfy!(isIntegral, I) && hasIndirections!(ElementEncodingType!T)) { ... } ``` Now let's see how it would look with my proposed long-form syntax. Since the improved shorter form of the syntax (`:modname:symbol`) now uses two colons, it makes sense to retroactively make the original longer form reflect that. Let it be an example of the [Mandela Effect](https://www.entitymag.com/mandela-effect-examples/). Let no one remember that I ever suggested doing anything else. :-) ```d auto uninitializedArray(T, I...)(I sizes) nothrow system if (import:std.traits:isDynamicArray!T && import:std.meta:allSatisfy!(import:std.traits:isIntegral, I) && import:std.traits:hasIndirections!(import:std.range:ElementEncodingType!T)) { ... } ``` This form is just as good if not better than `with(import x.y : z)`, and it can be used anywhere. -- An additional point of functionality regarding inline imports is that you can import a symbol in a [function contract](https://dlang.org/spec/function.html#contracts) without having to use the verbose form. So the clunky: ```d int func(A a, B b) in { import std.algorithm.comparison : cmp; assert(cmp(a,b) < 0); } do { // function body } ``` ...becomes: ```d int func(A a, B b) in (:std.algorithm.comparison: cmp(a, b) < 0) { // function body } ``` ------------------ The feature looks pretty good. The syntax looks good. But the one remaining concern is reusability. If inline imports are designed to be used exactly once without any further effect, many common use cases will find them too burdensome. ```d unittest { assert(:std.traits: sometest(myFunc, [1,2,3])); // you mean I have to import it every single time? assert(sometest(myFunc, [4,5,6])); // error: undefined symbol `sometest` assert(sometest(myFunc, [7,8,9])); // error: undefined symbol `sometest` // etc. } ``` What we really want is to be able to import the symbol inline, and then have it available as a regular symbol for the rest of the scope. The implementation of this feature could be as simple as a statement-level syntactic rewrite. So the above would be rewritten to: ```d unittest { import std.traits : sometest; // <-- inline import rewritten to assert(/* :std.traits: */ sometest(myFunc, [1,2,3])); // now we don't have to keep importing it assert(sometest(myFunc, [4,5,6])); assert(sometest(myFunc, [7,8,9])); // etc. } ``` What are the caveats of such a rewrite? Well, we would have to deal with the extremely rare case of a valid symbol changing its meaning due to a subsequent import in the same statement. Since defining the same variable twice within a function rightfully produces an error, this would only happen with symbols defined outside the function: ```d // a.d module a; int x = 4; ---------- // main.d int x = 3; void main() { { assert(x == 3); // import a : x; // <-- this is where the rewrite occurs assert(x + :a: x + x == 4 + 4 + 4); assert(x == 4); } assert(x == 3); } ``` The above code already compiles and runs as long as we rewrite the inline import to included the commented out line. Such a simple rewrite may be so easy to implement as a feature — and the alternative, more complex interpretation (`x + :a: x + x == 3 + 4 + 4`) so ill-conceived with regard to any coding style that could possibly be considered allowable — that the language can simply be specified to say that the above, statement-level syntactic rewrite is how inline imports work. -- Finally, we have to consider the "scope" of symbols imported by template constraints and other parts of the function signature. Symbols which are imported in the constraints, contracts, or just the regular signature (assuming you can import basic types there) should be made available for all the other parts of the function to access. Let's give a name to this greater space into which all these symbols are imported. Let's call it the "metascope" of the function. This simply means that inline imports are designed so that you don't have to import any symbol twice for the same function. So for example with this signature from phobos, the first import of `isInputRange` should put it into the function's metascope so there's no need to import it again: ```d auto cmp(R1, R2)(R1 r1, R2 r2) if (:std.range: isInputRange!R1 && /* :std.range:(<-- no need) */ isInputRange!R2) { ... } ```
Mar 15 2022