www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D import idiom compilation time

reply SrMordred <patric.dexheimer gmail.com> writes:
There is a veredict about the compilation speedup of the "New 
Import Idiom"?

Currently i´m using it this way:

struct _std
{
   template opDispatch(string moduleName)
   {
     mixin("import opDispatch = std." ~ moduleName ~ ";");
   }
}
...
//if used single time on scope.
_std.algorithm.max(a,b);

My question is that, in the long run, this will be worth the 
compilation time gains, or is just negligible and I should just 
import the normal way.
(and keep the code more sane)

Thanks!
Jan 03
next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 3 January 2019 at 23:54:42 UTC, SrMordred wrote:
 There is a veredict about the compilation speedup of the "New 
 Import Idiom"?

 Currently i´m using it this way:

 struct _std
 {
   template opDispatch(string moduleName)
   {
     mixin("import opDispatch = std." ~ moduleName ~ ";");
   }
 }
 ...
 //if used single time on scope.
 _std.algorithm.max(a,b);

 My question is that, in the long run, this will be worth the 
 compilation time gains, or is just negligible and I should just 
 import the normal way.
 (and keep the code more sane)

 Thanks!
Yes. --- testPerf.d struct fromStd { template opDispatch(string moduleName) { mixin("import opDispatch = std." ~ moduleName ~ ";"); } } version (UseFrom) { auto foo(T)(T arg) if (fromStd.typecons.isBitFlagEnum!T) { return arg; } } else { import std.typecons : isBitFlagEnum; auto foo(T)(T arg) if (isBitFlagEnum!T) { return arg; } } $ time dmd -c testperf.d real 0m0.050s $ time dmd -c -version=UseFrom testperf.d real 0m0.016s
Jan 03
parent Jonathan Marler <johnnymarler gmail.com> writes:
On Friday, 4 January 2019 at 02:43:01 UTC, Jonathan Marler wrote:
 On Thursday, 3 January 2019 at 23:54:42 UTC, SrMordred wrote:
 There is a veredict about the compilation speedup of the "New 
 Import Idiom"?

 Currently i´m using it this way:

 struct _std
 {
   template opDispatch(string moduleName)
   {
     mixin("import opDispatch = std." ~ moduleName ~ ";");
   }
 }
 ...
 //if used single time on scope.
 _std.algorithm.max(a,b);

 My question is that, in the long run, this will be worth the 
 compilation time gains, or is just negligible and I should 
 just import the normal way.
 (and keep the code more sane)

 Thanks!
Yes. --- testPerf.d struct fromStd { template opDispatch(string moduleName) { mixin("import opDispatch = std." ~ moduleName ~ ";"); } } version (UseFrom) { auto foo(T)(T arg) if (fromStd.typecons.isBitFlagEnum!T) { return arg; } } else { import std.typecons : isBitFlagEnum; auto foo(T)(T arg) if (isBitFlagEnum!T) { return arg; } } $ time dmd -c testperf.d real 0m0.050s $ time dmd -c -version=UseFrom testperf.d real 0m0.016s
I should clarify. It depends what type of code you are writing. If you are writing library code in which only small parts of modules may be used then importing this way will allow apps to use your library while only importing the modules they need. If you are writing program code where you don't need to expose subsets of code to other modules, you just want to use library code then in most cases there's no benefit to lazily importing other modules since you're always going to need them.
Jan 03
prev sibling next sibling parent reply Rubn <where is.this> writes:
On Thursday, 3 January 2019 at 23:54:42 UTC, SrMordred wrote:
 There is a veredict about the compilation speedup of the "New 
 Import Idiom"?

 Currently i´m using it this way:

 struct _std
 {
   template opDispatch(string moduleName)
   {
     mixin("import opDispatch = std." ~ moduleName ~ ";");
   }
 }
 ...
 //if used single time on scope.
 _std.algorithm.max(a,b);

 My question is that, in the long run, this will be worth the 
 compilation time gains, or is just negligible and I should just 
 import the normal way.
 (and keep the code more sane)

 Thanks!
I like this idiom way more than the other nasty from!"..." syntax. Though you need to define a struct for any library you'd want to use it with, not that big of a deal. Thanks for sharing.
Jan 04
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Friday, 4 January 2019 at 13:07:14 UTC, Rubn wrote:
 On Thursday, 3 January 2019 at 23:54:42 UTC, SrMordred wrote:
 There is a veredict about the compilation speedup of the "New 
 Import Idiom"?

 Currently i´m using it this way:

 struct _std
 {
   template opDispatch(string moduleName)
   {
     mixin("import opDispatch = std." ~ moduleName ~ ";");
   }
 }
 ...
 //if used single time on scope.
 _std.algorithm.max(a,b);

 My question is that, in the long run, this will be worth the 
 compilation time gains, or is just negligible and I should 
 just import the normal way.
 (and keep the code more sane)

 Thanks!
I like this idiom way more than the other nasty from!"..." syntax. Though you need to define a struct for any library you'd want to use it with, not that big of a deal. Thanks for sharing.
The struct could of course be templated: struct from(string namespace) { template opDispatch(string subnamespace) { mixin("import opDispatch = "~namespace~"."~subnamespace~";"); } } unittest { alias std = from!"std"; std.stdio.writeln("ohai"); } And slightly more fancy, for those pesky nested packages: struct from(string namespace) { static if (__traits(compiles, { mixin("import "~namespace~";"); })) { mixin("import __from = "~namespace~";"); } template opDispatch(string subnamespace) { static if (__traits(compiles, { mixin("import "~namespace~"."~subnamespace~";"); })) { alias opDispatch = .from!(namespace~"."~subnamespace); } else { mixin("alias opDispatch = __from."~subnamespace~";"); } } } unittest { alias std = from!"std"; std.stdio.writeln("ohai"); // This would be impossible with the first version: static assert(std.range.primitives.isInputRange!(int[])); // As would single-level imports: alias myLibrary = from!"myLibrary"; myLibrary.someFunction(); } -- Simen
Jan 04
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/4/19 1:04 PM, Simen Kjærås wrote:
[snip]

So I guess we could define a short module called "std.autostd" such that 
all uses of std.xxx would be resolved. Wow,
Jan 04
next sibling parent 0xEAB <desisma heidel.beer> writes:
On Saturday, 5 January 2019 at 01:58:15 UTC, Andrei Alexandrescu 
wrote:
 On 1/4/19 1:04 PM, Simen Kjærås wrote:
 [snip]

 So I guess we could define a short module called "std.autostd" 
 such that all uses of std.xxx would be resolved. Wow,
+1, awesome idea. - Elias
Jan 04
prev sibling parent reply Ethan <gooberman gmail.com> writes:
On Saturday, 5 January 2019 at 01:58:15 UTC, Andrei Alexandrescu 
wrote:
 On 1/4/19 1:04 PM, Simen Kjærås wrote:
 [snip]

 So I guess we could define a short module called "std.autostd" 
 such that all uses of std.xxx would be resolved. Wow,
Looking at the above examples, and my own programming patterns lately. Every time I need something, I add it to my import statement, ie import std.algorithm : min, max, map, remove, sort; Having each invocation import only that symbol defined at the end would be ideal. Something like: struct from( string thismodule ) { template opDispatch( string symbol ) { static if( __traits( compiles, { mixin( "import " ~ thismodule ~ ";" ); } ) && __traits( compiles, { mixin( "import " ~ thismodule ~ " : " ~ symbol ~ ";" ); } ) ) { mixin( "import " ~ thismodule ~ " : " ~ symbol ~ ";"); mixin( "alias opDispatch = " ~ symbol ~ ";" ); } else { alias opDispatch = from!( thismodule ~ "." ~ symbol ); } } } int main( string[] args ) { alias std = from!"std"; std.stdio.writeln( "It works!" ); return 0; }
Jan 04
parent Ethan <gooberman gmail.com> writes:
On Saturday, 5 January 2019 at 04:06:45 UTC, Ethan wrote:
 [code]
That first compiles check is unnecessary. This also gives stupid errors if you do something like std.stdio.wrteln( "It works!" ); eg. onlineapp.d(23): Error: more initializers than fields (0) of from
Jan 04
prev sibling parent reply bauss <jj_1337 live.dk> writes:
On Thursday, 3 January 2019 at 23:54:42 UTC, SrMordred wrote:
 struct _std
 {
   template opDispatch(string moduleName)
   {
     mixin("import opDispatch = std." ~ moduleName ~ ";");
   }
 }
There is nothing that's actually stopping you from just calling it std. It will work just as fine. That way you can end up with std.stdio.writeln("..."); Instead of: _std.stdio.writeln("...");
Jan 04
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/4/19 8:11 AM, bauss wrote:
 On Thursday, 3 January 2019 at 23:54:42 UTC, SrMordred wrote:
 struct _std
 {
   template opDispatch(string moduleName)
   {
     mixin("import opDispatch = std." ~ moduleName ~ ";");
   }
 }
There is nothing that's actually stopping you from just calling it std. It will work just as fine. That way you can end up with std.stdio.writeln("..."); Instead of: _std.stdio.writeln("...");
This is quite amazing.
Jan 04
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 04, 2019 at 08:54:44PM -0500, Andrei Alexandrescu via Digitalmars-d
wrote:
 On 1/4/19 8:11 AM, bauss wrote:
 On Thursday, 3 January 2019 at 23:54:42 UTC, SrMordred wrote:
 struct _std
 {
  template opDispatch(string moduleName)
  {
  mixin("import opDispatch = std." ~ moduleName ~ ";");
  }
 }
There is nothing that's actually stopping you from just calling it std. It will work just as fine. That way you can end up with std.stdio.writeln("..."); Instead of: _std.stdio.writeln("...");
This is quite amazing.
It doesn't have to be tied to a specific root package. Just call it "autoimport", or something short like "from": struct from { template opDispatch(string moduleName) { mixin("import opDispatch = " ~ moduleName ~ ";"); } } Then just write: from.std.stdio.writeln("..."); from.some.other.package.func(1, 2, 3); and so on. T -- Doubtless it is a good thing to have an open mind, but a truly open mind should be open at both ends, like the food-pipe, with the capacity for excretion as well as absorption. -- Northrop Frye
Jan 04
next sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Fri, 04 Jan 2019 20:13:05 -0800, H. S. Teoh wrote:
 It doesn't have to be tied to a specific root package. Just call it
 "autoimport", or something short like "from":
 
 	struct from {
 	  template opDispatch(string moduleName)
 	  {
 	    mixin("import opDispatch = " ~ moduleName ~ ";");
 	  }
 	}
 
 Then just write:
 
 	from.std.stdio.writeln("...");
Error: module `std` is in file 'std.d' which cannot be read You need the opDispatch to take the module name rather than a qualified package name. To fix that, you can write something like: --- struct FromImpl(string prefix) { template opDispatch(string ident) { enum qualified = prefix == "" ? ident : prefix ~ "." ~ ident; static if (__traits(compiles, {mixin("import ", qualified, ";");})) { mixin("import opDispatch = ", qualified, ";"); } else { enum opDispatch = FromImpl!(qualified)(); } } } enum from = FromImpl!""(); --- This is greedy, so it doesn't work for everything. Let's say you have: foo/ package.d bar/ baz.d In this case, from.foo.bar.baz.someFunc() will find foo/package.d and look for a symbol it defines named bar. If it doesn't define that symbol, you're out of luck. Reader exercise: is there a way to make this second case work? How does this interact with metaprogramming, and what sorts of precautions should you take?
Jan 04
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Jan 05, 2019 at 05:57:57AM +0000, Neia Neutuladh via Digitalmars-d
wrote:
 On Fri, 04 Jan 2019 20:13:05 -0800, H. S. Teoh wrote:
[...]
 	struct from {
 	 template opDispatch(string moduleName)
 	 {
 	 mixin("import opDispatch = " ~ moduleName ~ ";");
 	 }
 	}
 
 Then just write:
 
 	from.std.stdio.writeln("...");
Error: module `std` is in file 'std.d' which cannot be read You need the opDispatch to take the module name rather than a qualified package name.
Hmph. But in that case, doesn't that mean that the original definition of _std doesn't work with Phobos subpackages, like std.algorithm, because it would be trying to lookup the symbol in the package rather than the module? So something like _std.algorithm.searching.find would fail, because `searching.find` cannot be found in std/algorithm/package.d. [...]
 ---
 struct FromImpl(string prefix)
 {
     template opDispatch(string ident)
     {
         enum qualified = prefix == "" ? ident : prefix ~ "." ~ ident;
         static if (__traits(compiles,
            {mixin("import ", qualified, ";");}))
         {
             mixin("import opDispatch = ", qualified, ";");
         }
         else
         {
             enum opDispatch = FromImpl!(qualified)();
         }
     }
 }
 enum from = FromImpl!""();
 ---
Interesting.
 This is greedy, so it doesn't work for everything. Let's say you have:
 
   foo/
     package.d
     bar/
       baz.d
 
 In this case, from.foo.bar.baz.someFunc() will find foo/package.d and
 look for a symbol it defines named bar. If it doesn't define that
 symbol, you're out of luck.
 
 Reader exercise: is there a way to make this second case work? How
 does this interact with metaprogramming, and what sorts of precautions
 should you take?
Hmm. Couldn't you make it work by making it lazy? I.e., have opDispatch return a struct that encapsulates a partial package path, and that contains a further opDispatch to resolve the next component in the qualified name, and that only resolves to the actual symbol when it actually references an actual symbol? T -- There's light at the end of the tunnel. It's the oncoming train.
Jan 05
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Sat, 05 Jan 2019 06:52:14 -0800, H. S. Teoh wrote:
 Hmm. Couldn't you make it work by making it lazy? I.e., have opDispatch
 return a struct that encapsulates a partial package path, and that
 contains a further opDispatch to resolve the next component in the
 qualified name, and that only resolves to the actual symbol when it
 actually references an actual symbol?
Right! However, you can't tell the difference between accessing a symbol that doesn't exist and accessing a package. You'll see a difference in things like: static if (is(typeof(from.std.socket.Sockte))) In this lazy model, that's going to be a FromImpl!"std.socket.Sockte" and will pass. In the eager model, it would be std.socket.Sockte, which doesn't exist.
Jan 05
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/5/19 12:30 PM, Neia Neutuladh wrote:
 On Sat, 05 Jan 2019 06:52:14 -0800, H. S. Teoh wrote:
 Hmm. Couldn't you make it work by making it lazy? I.e., have opDispatch
 return a struct that encapsulates a partial package path, and that
 contains a further opDispatch to resolve the next component in the
 qualified name, and that only resolves to the actual symbol when it
 actually references an actual symbol?
Right! However, you can't tell the difference between accessing a symbol that doesn't exist and accessing a package.
Hmmm... we should have introspection mechanisms to distinguish these cases.
Jan 05
prev sibling parent reply Dgame <r.schuett.1987 gmail.com> writes:
On Saturday, 5 January 2019 at 17:30:15 UTC, Neia Neutuladh wrote:
 On Sat, 05 Jan 2019 06:52:14 -0800, H. S. Teoh wrote:
 Hmm. Couldn't you make it work by making it lazy? I.e., have 
 opDispatch return a struct that encapsulates a partial package 
 path, and that contains a further opDispatch to resolve the 
 next component in the qualified name, and that only resolves 
 to the actual symbol when it actually references an actual 
 symbol?
Right! However, you can't tell the difference between accessing a symbol that doesn't exist and accessing a package. You'll see a difference in things like: static if (is(typeof(from.std.socket.Sockte))) In this lazy model, that's going to be a FromImpl!"std.socket.Sockte" and will pass. In the eager model, it would be std.socket.Sockte, which doesn't exist.
I'm curious about that. Would that work or did I miss something? ---- template isSymbolInModule(string module_, string symbol) { static if (__traits(compiles, { mixin("import ", module_, ";"); })) { enum import_ = module_ ~ ":" ~ symbol; enum isSymbolInModule = __traits(compiles, { mixin("import ", import_, ";"); }); } else { enum isSymbolInModule = false; } } struct FromImpl(string module_) { template opDispatch(string symbol) { static if (isSymbolInModule!(module_, symbol)) { mixin("import ", module_, "; alias opDispatch = ", symbol, ";"); } else { enum import_ = module_.length == 0 ? symbol : module_ ~ "." ~ symbol; enum opDispatch = FromImpl!(import_)(); } } } from.std.stdio.writeln("Hallo"); auto _ = from.std.datetime.stopwatch.AutoStart.yes; ----
Jan 05
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Sat, 05 Jan 2019 21:14:37 +0000, Dgame wrote:
 I'm curious about that. Would that work or did I miss something?
In your version, I might write: from.std.stdio.thisFunctionDoesNotExist("Hallo"); And the error I would get is: Error: struct FromImpl does not overload ()
Jan 05
parent reply Dgame <r.schuett.1987 gmail.com> writes:
On Saturday, 5 January 2019 at 21:36:10 UTC, Neia Neutuladh wrote:
 On Sat, 05 Jan 2019 21:14:37 +0000, Dgame wrote:
 I'm curious about that. Would that work or did I miss 
 something?
In your version, I might write: from.std.stdio.thisFunctionDoesNotExist("Hallo"); And the error I would get is: Error: struct FromImpl does not overload ()
True.. What about: ---- template isModuleImport(string import_) { enum isModuleImport = __traits(compiles, { mixin("import ", import_, ";"); }); } template isSymbolInModule(string module_, string symbol) { static if (isModuleImport!module_) { enum import_ = module_ ~ ":" ~ symbol; enum isSymbolInModule = __traits(compiles, { mixin("import ", import_, ";"); }); } else { enum isSymbolInModule = false; } } template FailedSymbol(string symbol, string module_) { auto FailedSymbol(Args...)(auto ref Args args) { assert(0, "Symbol \"" ~ symbol ~ "\" not found in " ~ module_); } } struct FromImpl(string module_) { template opDispatch(string symbol) { static if (isSymbolInModule!(module_, symbol)) { mixin("import ", module_, "; alias opDispatch = ", symbol, ";"); } else { static if (module_.length == 0) { enum opDispatch = FromImpl!(symbol)(); } else { enum import_ = module_ ~ "." ~ symbol; static if (isModuleImport!import_) { enum opDispatch = FromImpl!(import_)(); } else { alias opDispatch = FailedSymbol!(symbol, module_); } } } } } enum from = FromImpl!null(); void main() { from.std.stdio.writeln("Hallo"); auto _ = from.std.datetime.stopwatch.AutoStart.yes; from.std.stdio.thisFunctionDoesNotExist("Hallo"); from.std.stdio.thisFunctionDoesNotExist(42); } ---- Output: ---- Hallo core.exception.AssertError onlineapp.d(20): Symbol "thisFunctionDoesNotExist" not found in std.stdio ---------------- ??:? _d_assert_msg [0x3c00dc54] onlineapp.d:20 pure nothrow nogc safe void onlineapp.FailedSymbol!("thisFunctionDoesNotExist", "std.stdio").FailedSymbol!(immutable(char)[]).FailedSymbol(immutable(char)[]) [0x3c00ceac] onlineapp.d:52 _Dmain [0x3c00c8c3] ----
Jan 05
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Jan 05, 2019 at 10:48:30PM +0000, Dgame via Digitalmars-d wrote:
[...]
 Output:
 
 ----
 Hallo
 core.exception.AssertError onlineapp.d(20): Symbol
 "thisFunctionDoesNotExist" not found in std.stdio
 ----------------
 ??:? _d_assert_msg [0x3c00dc54]
 onlineapp.d:20 pure nothrow  nogc  safe void
 onlineapp.FailedSymbol!("thisFunctionDoesNotExist",
 "std.stdio").FailedSymbol!(immutable(char)[]).FailedSymbol(immutable(char)[])
 [0x3c00ceac]
 onlineapp.d:52 _Dmain [0x3c00c8c3]
 ----
Impressive! Though we pretty much had to bring out the nuclear warhead to get here. :-D By this point I wonder if it has already made the original reason for doing this moot compared to just directly importing the symbol! :-P T -- They pretend to pay us, and we pretend to work. -- Russian saying
Jan 05
parent reply Daniel N <no public.email> writes:
On Saturday, 5 January 2019 at 23:44:31 UTC, H. S. Teoh wrote:
 On Sat, Jan 05, 2019 at 10:48:30PM +0000, Dgame via 
 Digitalmars-d wrote: [...]
 Output:
 
 ----
 Hallo
 core.exception.AssertError onlineapp.d(20): Symbol
 "thisFunctionDoesNotExist" not found in std.stdio
 ----------------
 ??:? _d_assert_msg [0x3c00dc54]
 onlineapp.d:20 pure nothrow  nogc  safe void
 onlineapp.FailedSymbol!("thisFunctionDoesNotExist",
 "std.stdio").FailedSymbol!(immutable(char)[]).FailedSymbol(immutable(char)[])
 [0x3c00ceac]
 onlineapp.d:52 _Dmain [0x3c00c8c3]
 ----
Impressive! Though we pretty much had to bring out the nuclear warhead to get here. :-D By this point I wonder if it has already made the original reason for doing this moot compared to just directly importing the symbol! :-P T
Nice, just surprised it took this long, I made the opDispatch snippet challenge in the blog nearly 2 years ago. I had assumed people would jump all over it. (like you now did in this thread). https://dlang.org/blog/2017/02/13/a-new-import-idiom/
 Now then, is this the only solution? No. As a challenge to the 
 reader, try to figure out what this does and, more importantly, 
 its flaw. Can you fix it?

 static struct STD
 {
   template opDispatch(string moduleName)
   {
     mixin("import opDispatch = std." ~ moduleName ~ ";");
   }
 }
Jan 06
parent reply FR86 <fr58426 gmail.com> writes:
On Sunday, 6 January 2019 at 12:11:41 UTC, Daniel N wrote:
 On Saturday, 5 January 2019 at 23:44:31 UTC, H. S. Teoh wrote:
 On Sat, Jan 05, 2019 at 10:48:30PM +0000, Dgame via 
 Digitalmars-d wrote: [...]
 Output:
 
 ----
 Hallo
 core.exception.AssertError onlineapp.d(20): Symbol
 "thisFunctionDoesNotExist" not found in std.stdio
 ----------------
 ??:? _d_assert_msg [0x3c00dc54]
 onlineapp.d:20 pure nothrow  nogc  safe void
 onlineapp.FailedSymbol!("thisFunctionDoesNotExist",
 "std.stdio").FailedSymbol!(immutable(char)[]).FailedSymbol(immutable(char)[])
 [0x3c00ceac]
 onlineapp.d:52 _Dmain [0x3c00c8c3]
 ----
Impressive! Though we pretty much had to bring out the nuclear warhead to get here. :-D By this point I wonder if it has already made the original reason for doing this moot compared to just directly importing the symbol! :-P T
Nice, just surprised it took this long, I made the opDispatch snippet challenge in the blog nearly 2 years ago. I had assumed people would jump all over it. (like you now did in this thread).
Really nice! I like it so much, I've taken the liberty to wrap it up as a dub package: https://code.dlang.org/packages/localimport. As I'm a total noob on all this, please let me know if I messed anything up or stepped on anybody's toes with this. FR
Jan 06
parent reply Daniel N <no public.email> writes:
On Sunday, 6 January 2019 at 15:45:57 UTC, FR86 wrote:
 Really nice! I like it so much, I've taken the liberty to wrap 
 it up as a dub package: 
 https://code.dlang.org/packages/localimport.
 As I'm a total noob on all this, please let me know if I messed 
 anything up or stepped on anybody's toes with this.

 FR
Please feel free to use it any way you wish. Boost/Public Domain etc, all fine. Personally I prefer "static assert" over exceptions.
Jan 06
parent reply FR86 <fr58426 gmail.com> writes:
On Sunday, 6 January 2019 at 17:36:44 UTC, Daniel N wrote:

 Personally I prefer "static assert" over exceptions.
Makes sense! Updated to use static assert outside of unittests.
Jan 06
parent reply Seb <seb wilzba.ch> writes:
On Sunday, 6 January 2019 at 20:26:49 UTC, FR86 wrote:
 On Sunday, 6 January 2019 at 17:36:44 UTC, Daniel N wrote:

 Personally I prefer "static assert" over exceptions.
Makes sense! Updated to use static assert outside of unittests.
`version(unittest)` will also be defined if a user uses your library for testing his code. If you really want to, you could set a special version via dub, e.g.: ``` configuration "unittest" { version "LocalImportUnittest" } ``` Also, you need to e.g. split up your test into two as otherwise you won't test whether the first part of the test (or the second for that matter) actually throws. You are currently doing an XOR in both parts.
Jan 07
parent FR86 <fr58426 gmail.com> writes:
On Monday, 7 January 2019 at 18:11:51 UTC, Seb wrote:
 `version(unittest)` will also be defined if a user uses your 
 library for testing his code. If you really want to, you could 
 set a special version via dub, e.g.:

 ```
 configuration "unittest" {
     version "LocalImportUnittest"
 }
 ```
Done!
 Also, you need to e.g. split up your test into two as otherwise 
 you won't test whether the first part of the test (or the 
 second for that matter) actually throws. You are currently 
 doing an XOR in both parts.
Oops! Fixed. Thanks for the feedback, much appreciated!
Jan 07
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/5/19 5:48 PM, Dgame wrote:
 On Saturday, 5 January 2019 at 21:36:10 UTC, Neia Neutuladh wrote:
 On Sat, 05 Jan 2019 21:14:37 +0000, Dgame wrote:
 I'm curious about that. Would that work or did I miss something?
In your version, I might write:   from.std.stdio.thisFunctionDoesNotExist("Hallo"); And the error I would get is:   Error: struct FromImpl does not overload ()
True.. What about: ---- template isModuleImport(string import_) {     enum isModuleImport = __traits(compiles, { mixin("import ", import_, ";"); }); } template isSymbolInModule(string module_, string symbol) {     static if (isModuleImport!module_) {         enum import_ = module_ ~ ":" ~ symbol;         enum isSymbolInModule = __traits(compiles, { mixin("import ", import_, ";"); });     } else {         enum isSymbolInModule = false;     } } template FailedSymbol(string symbol, string module_) {     auto FailedSymbol(Args...)(auto ref Args args)     {         assert(0, "Symbol \"" ~ symbol ~ "\" not found in " ~ module_);     } } struct FromImpl(string module_) {     template opDispatch(string symbol)     {         static if (isSymbolInModule!(module_, symbol)) {             mixin("import ", module_, "; alias opDispatch = ", symbol, ";");         } else {             static if (module_.length == 0) {                 enum opDispatch = FromImpl!(symbol)();             } else {                 enum import_ = module_ ~ "." ~ symbol;                 static if (isModuleImport!import_) {                        enum opDispatch = FromImpl!(import_)();                 } else {                     alias opDispatch = FailedSymbol!(symbol, module_);                 }             }         }     } } enum from = FromImpl!null(); void main() {     from.std.stdio.writeln("Hallo");     auto _ = from.std.datetime.stopwatch.AutoStart.yes;     from.std.stdio.thisFunctionDoesNotExist("Hallo");     from.std.stdio.thisFunctionDoesNotExist(42); } ---- Output: ---- Hallo core.exception.AssertError onlineapp.d(20): Symbol "thisFunctionDoesNotExist" not found in std.stdio ---------------- ??:? _d_assert_msg [0x3c00dc54] onlineapp.d:20 pure nothrow nogc safe void onlineapp.FailedSymbol!("thisFunctionDoesNotExist", "std.stdio").FailedSymbol!(immutable(char)[]).FailedSymbol(immutable(char)[]) [0x3c00ceac] onlineapp.d:52 _Dmain [0x3c00c8c3] ----
This is pretty awesome. I ran a couple of experiments and the idiom seems to be lazy as expected, e.g. in the declaration: void fun(T)() if (from.std.traits.isIntegral!T) { from.std.stdio.writeln("Hallo"); } neither std.traits nor std.stdio gets loaded if fun is not instantiated. I took the liberty to ask Razvan Nitu to look into fixing https://issues.dlang.org/show_bug.cgi?id=17181. I also asked Eduard Staniloiu to investigate using this idiom internally in Phobos so as to accumulate some experience with it. Dgame's implementation is a good start. Since the implementation would (initially at least) be private, the presence of "from" in documentation would be awkward. So I suggest the glorious hack: alias std = from.std; so then things like std.traits.isIntegral and std.algorithm.comparison.min resolve and auto-load properly, while also standing beautifully in the documentation. Andrei
Jan 07
next sibling parent reply Martin Tschierschke <mt smartdolphin.de> writes:
On Monday, 7 January 2019 at 21:50:20 UTC, Andrei Alexandrescu 
wrote:
 On 1/5/19 5:48 PM, Dgame wrote:
[..]
 So I suggest the glorious hack:

 alias std = from.std;

 so then things like std.traits.isIntegral and 
 std.algorithm.comparison.min resolve and auto-load properly, 
 while also standing beautifully in the documentation.


 Andrei
I just started playing around with it, it might be no surprise, but if you go further and start to make some local aliases inside main() like alias writeln = std.stdio.writeln; // for convenience string myvar ="Test"; You can user writeln(myvar) but not with UFCS: myvar.writeln; I realized that UFCS would work only if the alias is defined outside of main(); Is this a bug or a feature? Don't know if this can be solved. Regards mt. Regards mt.
Jan 08
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 8 January 2019 at 10:54:24 UTC, Martin Tschierschke 
wrote:
 I realized that UFCS would work only if the alias is defined 
 outside of main();

 Is this a bug or a feature? Don't know if this can be solved.
It is by design - UFCS only considered for global symbols. The idea is to simplify the reasoning process for finding .members by eliminating local scopes from the search (supposed to make ide stuff easier, etc) Whether this is good design or not is left up to the reader.
Jan 08
parent reply Martin Tschierschke <mt smartdolphin.de> writes:
On Tuesday, 8 January 2019 at 13:40:51 UTC, Adam D. Ruppe wrote:
[...]
 It is by design - UFCS only considered for global symbols. The 
 idea is to simplify the reasoning process for finding .members 
 by eliminating local scopes from the search (supposed to make 
 ide stuff easier, etc)

 Whether this is good design or not is left up to the reader.
So, as a result I can conclude, that real local imports don't work togethter with UFCS, and this is disabling any chaining.
Jan 08
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jan 08, 2019 at 02:13:21PM +0000, Martin Tschierschke via Digitalmars-d
wrote:
 On Tuesday, 8 January 2019 at 13:40:51 UTC, Adam D. Ruppe wrote:
 [...]
 It is by design - UFCS only considered for global symbols. The idea
 is to simplify the reasoning process for finding .members by
 eliminating local scopes from the search (supposed to make ide stuff
 easier, etc)
 
 Whether this is good design or not is left up to the reader.
So, as a result I can conclude, that real local imports don't work togethter with UFCS, and this is disabling any chaining.
Not true. This works: void main() { // N.B.: local imports import std.array; import std.algorithm; import std.conv; import std.stdio; // N.B.: UFCS chaining "abcdef".map!(e => cast(int) (e - 'a')) .filter!(e => e < 4) .map!(e => cast(dchar) (e + 'm')) .to!string .writeln; } To state more accurately what Adam said, UFCS is considered for *module-level* symbols. Meaning, the requirement is applied at the site of definition (it has to be at the top *level* in the module where it's defined) rather than the site of import (which can be in a local *scope*). T -- What is Matter, what is Mind? Never Mind, it doesn't Matter.
Jan 08
parent reply Martin Tschierschke <mt smartdolphin.de> writes:
On Tuesday, 8 January 2019 at 14:28:47 UTC, H. S. Teoh wrote:
 On Tue, Jan 08, 2019 at 02:13:21PM +0000, Martin Tschierschke 
 via Digitalmars-d wrote:
 [...]
Not true. This works: void main() { // N.B.: local imports import std.array; import std.algorithm; import std.conv; import std.stdio; // N.B.: UFCS chaining "abcdef".map!(e => cast(int) (e - 'a')) .filter!(e => e < 4) .map!(e => cast(dchar) (e + 'm')) .to!string .writeln; } To state more accurately what Adam said, UFCS is considered for *module-level* symbols. Meaning, the requirement is applied at the site of definition (it has to be at the top *level* in the module where it's defined) rather than the site of import (which can be in a local *scope*). T
OK. Thank you! With "real local import" I was referring to the "new import idiom" in this thread, provided by localimport.dub.pm mt.
Jan 08
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jan 08, 2019 at 03:18:03PM +0000, Martin Tschierschke via Digitalmars-d
wrote:
[...]
 With "real local import" I was referring to the "new import idiom" in
 this thread, provided by  localimport.dub.pm
[...] The intent of that import idiom is an import whose scope is limited to the expression it's in. It's a way of pulling in a specific symbol without "polluting" the surrounding scope. E.g. currently, if you have a module-scope declaration that requires an imported symbol, you have no choice but to import the symbol into the containing scope: // This import line is required otherwise the sig constraint // won't work. import std.range.primitives : isInputRange; auto myRangeFunc(R)(R range) if (isInputRange!R) { ... } If this is the only function in a module of say 100 functions that requires std.range.primitives, it seems wasteful to import it into the entire module's scope. With the new import idiom, we can do this: // N.B.: no more imports in module scope! auto myRangeFunc(R)(R range) if (from.std.range.isInputRange!R) { ... } Which also makes the declaration carry it's own import dependencies, so when you decide to refactor and move this function into another module, you'll never have to worry about also moving/copying the global import statement. This is the primary use case of this idiom. If you actually need the import symbols into the containing scope, just use the built-in import statement instead! :-D auto myRangeFunc(R)(R range) if (from.std.range.isInputRange!R) { // We need std.uni in this function, so just pull it // into the function scope. No need to pull out the // expression-local import warhead! import std.uni : isWhite; while (!range.empty && isWhite(range.front)) range.popFront; ... // more uses of isWhite here ... } T -- LINUX = Lousy Interface for Nefarious Unix Xenophobes.
Jan 08
prev sibling next sibling parent reply JN <666total wp.pl> writes:
On Monday, 7 January 2019 at 21:50:20 UTC, Andrei Alexandrescu 
wrote:
 This is pretty awesome.

 Since the implementation would (initially at least) be private, 
 the presence of "from" in documentation would be awkward. So I 
 suggest the glorious hack:
I am probably in minority here, or I am misunderstand the purpose of this feature, but wouldn't such stuff be better put somewhere around the compiler/frontend rather than in the library with compile-time magic? Maybe I am wrong, but if I were a complete outsider to D and saw this kind of mixin magic, I'd be definitely concerned. I wouldn't see it as "glorious hack", instead I'd view it as "wow, D is so bad, look how many tricks they do just to import std lib" :S
Jan 08
parent reply Johan Engelen <j j.nl> writes:
On Tuesday, 8 January 2019 at 13:39:48 UTC, JN wrote:
 On Monday, 7 January 2019 at 21:50:20 UTC, Andrei Alexandrescu 
 wrote:
 This is pretty awesome.

 Since the implementation would (initially at least) be 
 private, the presence of "from" in documentation would be 
 awkward. So I suggest the glorious hack:
I am probably in minority here, or I am misunderstand the purpose of this feature, but wouldn't such stuff be better put somewhere around the compiler/frontend rather than in the library with compile-time magic?
FYI: at the end of last year Eyal (Weka) and Andrei (and me a little bit) have been discussing lazy importing (full import) of symbols by the compiler. We hope to have a student work on this. Indeed I agree with you: the compiler could do (some of) this stuff, and because the rewards seem large it makes sense to indeed have the compiler do it. -Johan
Jan 08
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, January 8, 2019 7:40:47 AM MST Johan Engelen via Digitalmars-d 
wrote:
 On Tuesday, 8 January 2019 at 13:39:48 UTC, JN wrote:
 On Monday, 7 January 2019 at 21:50:20 UTC, Andrei Alexandrescu

 wrote:
 This is pretty awesome.

 Since the implementation would (initially at least) be
 private, the presence of "from" in documentation would be
 awkward. So I suggest the glorious hack:
I am probably in minority here, or I am misunderstand the purpose of this feature, but wouldn't such stuff be better put somewhere around the compiler/frontend rather than in the library with compile-time magic?
FYI: at the end of last year Eyal (Weka) and Andrei (and me a little bit) have been discussing lazy importing (full import) of symbols by the compiler. We hope to have a student work on this. Indeed I agree with you: the compiler could do (some of) this stuff, and because the rewards seem large it makes sense to indeed have the compiler do it.
Yeah, Walter has expressed interest in having lazy imports in the past (e.g. he talked about it when Liran was complaining about importing issues at dconf a few years ago). So, if it were implemented, I wouldn't expect it to have much trouble getting merged. Personally, I've argued every time this sort of solution has come up that it's incredibly ugly and tedious and that we should just do lazy imports if possible, because they would cleanly fix the compilation times without requiring such verbose workarounds. But of course, someone has to actually do the work of implementing lazy imports. - Jonathan M Davis
Jan 08
prev sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Tuesday, 8 January 2019 at 14:40:47 UTC, Johan Engelen wrote:
 On Tuesday, 8 January 2019 at 13:39:48 UTC, JN wrote:
 On Monday, 7 January 2019 at 21:50:20 UTC, Andrei Alexandrescu 
 wrote:
 This is pretty awesome.

 Since the implementation would (initially at least) be 
 private, the presence of "from" in documentation would be 
 awkward. So I suggest the glorious hack:
I am probably in minority here, or I am misunderstand the purpose of this feature, but wouldn't such stuff be better put somewhere around the compiler/frontend rather than in the library with compile-time magic?
FYI: at the end of last year Eyal (Weka) and Andrei (and me a little bit) have been discussing lazy importing (full import) of symbols by the compiler. We hope to have a student work on this. Indeed I agree with you: the compiler could do (some of) this stuff, and because the rewards seem large it makes sense to indeed have the compiler do it. -Johan
Make sure whoever works on it is aware of this: https://github.com/marler8997/dlangfeatures#lazy-imports To summarize, there's no way to make normal imports lazy (i.e. "import foo;"). However we can make the following lazy by default: 1. static imports (static import std.typecons;) 2. selective imports (import std.typecons: isBigEndian;) 3. aliased imports (import t = std.typecons;) I had a partial implementation for lazy imports, but Walter never reviewed my other pull requests so I never pushed it.
Jan 09
parent reply Dgame <r.schuett.1987 gmail.com> writes:
On Wednesday, 9 January 2019 at 22:39:58 UTC, Jonathan Marler 
wrote:
 Make sure whoever works on it is aware of this:

 https://github.com/marler8997/dlangfeatures#lazy-imports

 To summarize, there's no way to make normal imports lazy (i.e. 
 "import foo;"). However we can make the following lazy by 
 default:

 1. static imports (static import std.typecons;)
 2. selective imports (import std.typecons: isBigEndian;)
 3. aliased imports (import t = std.typecons;)

 I had a partial implementation for lazy imports, but Walter 
 never reviewed my other pull requests so I never pushed it.
Maybe I'm missing something, but wouldn't it be possible to lazy import `import foo;` if the compiler checks lazily if a symbol isn't found in the current scope and only then tries to find it in one of the imports? Or would that be ineffective? Example: ---- import foo; // contains bar2 void bar1() { } bar1(); // in current scope bar2(); // not found, we have to search in the imports ----
Jan 09
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Wed, 09 Jan 2019 22:59:59 +0000, Dgame wrote:
 Maybe I'm missing something, but wouldn't it be possible to lazy import
 `import foo;` if the compiler checks lazily if a symbol isn't found in
 the current scope and only then tries to find it in one of the imports?
 Or would that be ineffective?
If you import two modules and both define functions named `write`, it is an error to use that function without somehow disambiguating. With every module import being lazy, it would only be an error if you had previously used symbols from both of those modules, and otherwise you'd get the function from whichever module has already been loaded.
Jan 09
parent Jonathan Marler <johnnymarler gmail.com> writes:
On Wednesday, 9 January 2019 at 23:14:09 UTC, Neia Neutuladh 
wrote:
 On Wed, 09 Jan 2019 22:59:59 +0000, Dgame wrote:
 Maybe I'm missing something, but wouldn't it be possible to 
 lazy import `import foo;` if the compiler checks lazily if a 
 symbol isn't found in the current scope and only then tries to 
 find it in one of the imports? Or would that be ineffective?
If you import two modules and both define functions named `write`, it is an error to use that function without somehow disambiguating. With every module import being lazy, it would only be an error if you had previously used symbols from both of those modules, and otherwise you'd get the function from whichever module has already been loaded.
That's right. Basically it boils down to this, as soon as you need to resolve the first symbol that isn't defined in the current module, you now have to load "ALL NORMAL IMPORTS" in the current scope; import foo, bar, baz; void func1() { } func1(); // don't need to import any modules func2(); // now you need to load all normal imports, foo, bar and baz. // even if you find func2 in the first module, you still have // to load them all because they could both define it and they // may either be ambiguous or one may have an overload that // matches better. plus, you have to load them all in order to // keep module order "invariant".
Jan 09
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Monday, 7 January 2019 at 21:50:20 UTC, Andrei Alexandrescu 
wrote:
 the presence of "from" in documentation would be awkward. So I 
 suggest the glorious hack:

 alias std = from.std;
Apart from compiler overhead, this seems to make `static import` redundant.
Jan 08
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/4/19 11:13 PM, H. S. Teoh wrote:
 On Fri, Jan 04, 2019 at 08:54:44PM -0500, Andrei Alexandrescu via
Digitalmars-d wrote:
 On 1/4/19 8:11 AM, bauss wrote:
 On Thursday, 3 January 2019 at 23:54:42 UTC, SrMordred wrote:
 struct _std
 {
    template opDispatch(string moduleName)
    {
      mixin("import opDispatch = std." ~ moduleName ~ ";");
    }
 }
There is nothing that's actually stopping you from just calling it std. It will work just as fine. That way you can end up with std.stdio.writeln("..."); Instead of: _std.stdio.writeln("...");
This is quite amazing.
It doesn't have to be tied to a specific root package. Just call it "autoimport", or something short like "from": struct from {   template opDispatch(string moduleName)   {     mixin("import opDispatch = " ~ moduleName ~ ";");   } } Then just write: from.std.stdio.writeln("..."); from.some.other.package.func(1, 2, 3); and so on. T
Cool. Here's the previous clunkier implementation: https://github.com/dlang/druntime/pull/1756 And here's the bug preventing it: https://issues.dlang.org/show_bug.cgi?id=17181 Is the bug also affecting this newly invented idiom? Andrei
Jan 05