www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - lazy import

reply xlogor <ram.az gmail.com> writes:


I’d like to propose a DIP for adding lazy imports to D.


D already has the `lazy` keyword for function parameters, which 
works wonders:
- Arguments are only evaluated when (and if) actually used.
- This avoids unnecessary work, prevents allocations, and gives a 
nice middle ground between eager and manual `delegate` passing.

It’s a proven feature that shows how well deferred evaluation 
fits into the language.

Currently, though, `import` in D is eager: all imported modules 
are parsed and semantically analyzed during compilation, 
regardless of whether their symbols are actually referenced. This 
can cause unnecessary compile-time costs, especially in large 
projects with deep dependency graphs.

A **lazy import** would extend the same philosophy of deferred 
evaluation to the module system: the compiler would defer full 
analysis of a module until it is actually needed (when a symbol 
from it is referenced). This reduces compile times and makes 
dependency management more efficient.

Proposed Syntax

```d
lazy import std.big;
```

This would mean:

The compiler does not fully load/parse `std.big` at import time.

If no symbols from `std.big` are used, the module never impacts 
compilation.

If symbols are referenced, the compiler processes the module as 
usual.

Alternatively, the same effect could be achieved with an 
attribute-style approach:
```d
 lazy import std.big;
```
Oct 03
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 3 October 2025 at 19:14:00 UTC, xlogor wrote:
 A **lazy import** would extend the same philosophy of deferred 
 evaluation to the module system: the compiler would defer full 
 analysis of a module until it is actually needed (when a symbol 
 from it is referenced). This reduces compile times and makes 
 dependency management more efficient.
How will the compiler know whether a symbol from the module is referenced without parsing and semantically analyzing the module?
Oct 03
parent reply xlogor <ram.az gmail.com> writes:
On Friday, 3 October 2025 at 19:30:29 UTC, Paul Backus wrote:
 On Friday, 3 October 2025 at 19:14:00 UTC, xlogor wrote:
 A **lazy import** would extend the same philosophy of deferred 
 evaluation to the module system: the compiler would defer full 
 analysis of a module until it is actually needed (when a 
 symbol from it is referenced). This reduces compile times and 
 makes dependency management more efficient.
How will the compiler know whether a symbol from the module is referenced without parsing and semantically analyzing the module?
Perhaps this could be limited to renamed/static imports? ```D lazy import big = std.big; ```
Oct 04
parent Paul Backus <snarwin gmail.com> writes:
On Saturday, 4 October 2025 at 08:54:16 UTC, xlogor wrote:
 On Friday, 3 October 2025 at 19:30:29 UTC, Paul Backus wrote:
 On Friday, 3 October 2025 at 19:14:00 UTC, xlogor wrote:
 A **lazy import** would extend the same philosophy of 
 deferred evaluation to the module system: the compiler would 
 defer full analysis of a module until it is actually needed 
 (when a symbol from it is referenced). This reduces compile 
 times and makes dependency management more efficient.
How will the compiler know whether a symbol from the module is referenced without parsing and semantically analyzing the module?
Perhaps this could be limited to renamed/static imports? ```D lazy import big = std.big; ```
I suppose that could work. We could also consider treating renamed and static imports this way by default, without requiring the `lazy` keyword.
Oct 04
prev sibling next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Friday, 3 October 2025 at 19:14:00 UTC, xlogor wrote:


 I’d like to propose a DIP for adding lazy imports to D.


 D already has the `lazy` keyword for function parameters, which 
 works wonders:
 - Arguments are only evaluated when (and if) actually used.
 - This avoids unnecessary work, prevents allocations, and gives 
 a nice middle ground between eager and manual `delegate` 
 passing.

 It’s a proven feature that shows how well deferred evaluation 
 fits into the language.

 Currently, though, `import` in D is eager: all imported modules 
 are parsed and semantically analyzed during compilation, 
 regardless of whether their symbols are actually referenced. 
 This can cause unnecessary compile-time costs, especially in 
 large projects with deep dependency graphs.

 A **lazy import** would extend the same philosophy of deferred 
 evaluation to the module system: the compiler would defer full 
 analysis of a module until it is actually needed (when a symbol 
 from it is referenced). This reduces compile times and makes 
 dependency management more efficient.

 Proposed Syntax

 ```d
 lazy import std.big;
 ```

 This would mean:

 The compiler does not fully load/parse `std.big` at import time.

 If no symbols from `std.big` are used, the module never impacts 
 compilation.

 If symbols are referenced, the compiler processes the module as 
 usual.

 Alternatively, the same effect could be achieved with an 
 attribute-style approach:
 ```d
  lazy import std.big;
 ```
All templates functions already are lazy, the std is slow because they get initialized; its a lack of initialization discipline combined with a worse of both worlds policys on templates.
Oct 03
parent reply xlogor <ram.az gmail.com> writes:
On Friday, 3 October 2025 at 20:18:45 UTC, monkyyy wrote:
 On Friday, 3 October 2025 at 19:14:00 UTC, xlogor wrote:


 I’d like to propose a DIP for adding lazy imports to D.


 D already has the `lazy` keyword for function parameters, 
 which works wonders:
 - Arguments are only evaluated when (and if) actually used.
 - This avoids unnecessary work, prevents al__cpLocations, and 
 gives a nice middle ground between eager and manual `delegate` 
 passing.

 It’s a proven feature that shows how well deferred evaluation 
 fits into the language.

 Currently, though, `import` in D is eager: all imported 
 modules are parsed and semantically analyzed during 
 compilation, regardless of whether their symbols are actually 
 referenced. This can cause unnecessary compile-time costs, 
 especially in large projects with deep dependency graphs.

 A **lazy import** would extend the same philosophy of deferred 
 evaluation to the module system: the compiler would defer full 
 analysis of a module until it is actually needed (when a 
 symbol from it is referenced). This reduces compile times and 
 makes dependency management more efficient.

 Proposed Syntax

 ```d
 lazy import std.big;
 ```

 This would mean:

 The compiler does not fully load/parse `std.big` at import 
 time.

 If no symbols from `std.big` are used, the module never 
 impacts compilation.

 If symbols are referenced, the compiler processes the module 
 as usual.

 Alternatively, the same effect could be achieved with an 
 attribute-style approach:
 ```d
  lazy import std.big;
 ```
All templates functions already are lazy, the std is slow because they get initialized; its a lack of initialization discipline combined with a worse of both worlds policys on templates.
D compilers lack incremental compilation; lazy evaluation should be explored further.
Oct 04
parent monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 4 October 2025 at 08:56:47 UTC, xlogor wrote:
 On Friday, 3 October 2025 at 20:18:45 UTC, monkyyy wrote:
 

 All templates functions already are lazy, the std is slow 
 because they get initialized; its a lack of initialization 
 discipline combined with a worse of both worlds policys on 
 templates.
D compilers lack incremental compilation; lazy evaluation should be explored further.
Memorization only provides speed ups when it effects the whole pipeline. "Incremental compilation" which already exists and means a terrible idea where you build libs separately. Push vs pull logistics clash and can easily produce worse of both world results. The c abi is push logistics, theres a push of files you given the compiler with headers and all golbal scope names these get pushed into a pipeline and a executable come out the other end. Templates, out of order evulation and in a sense modules, are pull logistics; modules need to be parsed but a (pure) template is not initialized as a named symbol until used, lerp!Rect maybe be valid but it doesn't exist until used, pulled. --- The std and user base have a mix of takes on either embracing c norms or going with templates, you cant fix this with features. The std needs to have a compilation life time theory(we will see if the current plan improve things, Im mildly hopeful that the core vs upper end split will work by accident, but the core team are anti template) The user base needs education or do find out what the hell these 90 minute compiles from a small minority come from. --- We should go full "pull"; and incremental compilation is a "push" (anti-)feature. All imports are already lazy as they can be.
 lazy import big = std.big;
If you want a lazy namespace so a import doesnt exist until called you could try ```d template big(){ import big; } ``` Then you call `big!().writeln;` but this breaks the usual overload rules and ufcs so it cant be made a default behavior.
Oct 04
prev sibling next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
A proposal has been made for Python to add lazy imports.

https://pep-previews--4622.org.readthedocs.build/pep-0810/

These appear to be equivalent to our static imports + only evaluate that 
module on use.

-----

If a module is passed on the command line (and is a root), it cannot be 
lazy. It will be semantically analyzed in order.

This could only apply to import paths.

Furthermore the way the compiler works is in stages, a function body 
will be analyzed either when its required or after everything else in 
the module has been. For imports this extra step for function bodies is 
meant to be elided.

For this reason I see minimal benefit as it currently stands, given the 
above definitions.

Proof:

```d
module def;

void func() {
         sf;
}
```

```d
module app;
import def;

void main() {
         func;
}
```

```
$ dmd main.d
main.obj : error LNK2019: unresolved external symbol _D3def4funcFZv 
referenced in function _Dmain
main.exe : fatal error LNK1120: 1 unresolved externals
Error: linker exited with status 1120
        C:\Program Files\Microsoft Visual 
Studio\2022\Community\VC\Tools\MSVC\14.41.34120\bin\HostX64\x64\link.exe 
/NOLOGO "main.obj"   /DEFAULTLIB:phobos64  /LIBPATH:"C:\Program 
Files\Microsoft Visual 
Studio\2022\Community\VC\Tools\MSVC\14.41.34120\lib\x64" 
legacy_stdio_definitions.lib /LIBPATH:"C:\Program Files (x86)\Windows 
Kits\10\Lib\10.0.20348.0\ucrt\x64" /LIBPATH:"C:\Program Files 
(x86)\Windows Kits\10\lib\10.0.20348.0\um\x64"
Error: undefined reference to `void def.func()`
        referenced from `_Dmain`
        perhaps `.d` files need to be added on the command line, or use 
`-i` to compile imports
```
Oct 04
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Friday, 3 October 2025 at 19:14:00 UTC, xlogor wrote:
 A **lazy import** would extend the same philosophy of deferred 
 evaluation to the module system: the compiler would defer full 
 analysis of a module until it is actually needed (when a symbol 
 from it is referenced). This reduces compile times and makes 
 dependency management more efficient.

 Proposed Syntax

 ```d
 lazy import std.big;
 ```

 This would mean:

 The compiler does not fully load/parse `std.big` at import time.

 If no symbols from `std.big` are used, the module never impacts 
 compilation.

 If symbols are referenced, the compiler processes the module as 
 usual.
**TL;DR:** You need static or selective imports. For “normal” imports, it can’t be done. Any symbol that’s encountered could be from the module. Without parsing that, it cannot be ruled out. Even if it is found in another module, `std.big` could contain the viable overloads (ideally the only ones, then, to avoid an error). It can only work if that makes `std.big` implicitly a static import. I’d suggest `lazy` requires `static` in that case, so it’s clearer. Such a restriction could be lifted later. Another option is to require selective imports. Contrary to `import std.big` (be it lazy or not), in `import std.big : stick`, it can be determined if a symbol referenced might be imported from `std.big` since you stated in the importing module what you import from `std.big`. Only if you end up “using” one of the symbols, the module has to be parsed.
Oct 09