NixOS / NixOS/nix

Problems related to `scopedImport`

Open
#8,024 2 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

documentation language question
Dominant language
C++
Stars
17.7k
Forks
2k
Avg merge
1d 16h
Merged PRs (30d)
80

Description

Confusing free variables

A bit more problematic is the fact that files called with a scopedImport have free variables, which can be confusing as to where they're actually defined, compared to a function argument at the top where it's clear that the variables are passed in by a function call.

This is in my opinion the main reason we might consider removing it.

Doesn't allow file evaluation to be cached

Here's an example where scopedImport can't cache files the same way import can. This is how evaluation is cached with import:

# function.nix
let
  b = builtins.trace "calculating b" (1 + 1);
in { a }: a + b

# call-function.nix
import ./function.nix { a = 1; }
*
import ./function.nix { a = 1; }

Note how calculating b is only printed once:

$ nix-instantiate --eval call-function.nix
trace: calculating b
9

Trying the same with scopedImport:

# scoped.nix
let
  b = builtins.trace "calculating b" (1 + 1);
in a + b

# import-scoped.nix
builtins.scopedImport { a = 1; } ./scoped.nix
*
builtins.scopedImport { a = 1; } ./scoped.nix

Now calculating b is printed twice:

$ nix-instantiate --eval import-scoped.nix
trace: calculating b
trace: calculating b
9

But this still makes sense if you think of scopedImport as just importing the file as the function body, prepending an attribute argument to it and then calling it, so like this:

# uncached.nix
{ a }: # This line would be prepended implicitly
let
  b = builtins.trace "calculating b" (1 + 1);
in a + b

# call-uncached.nix
import ./uncached.nix { a = 1; }
*
import ./uncached.nix { a = 1; }

which will also calculate b twice:

$ nix-instantiate --eval call-uncached.nix
trace: calculating b
trace: calculating b
9

So I don't think these semantics are problematic, as long as the docs clearly explain them.

Very related is https://github.com/NixOS/nix/issues/6228 for caching function application.

Combined with the ability to shadow builtins

However another more fundamental problem is that builtins can be shadowed:

let
  import = file: "Not importing ${toString file}";
in import ./file.nix

Which when combined with scopedImport can make all import's not be able to cache evaluation anymore:

# expr.nix
builtins.trace "Evaluating expr.nix" {
  nested = import ./expr.nix;
}

# scopedImport.nix
let
  scope = {
    import = file: builtins.scopedImport scope file;
  };
in builtins.scopedImport scope ./expr.nix

# normalImport.nix
import ./expr.nix
$ nix-instantiate --eval scopedImport.nix -A nested.nested.nested
trace: Evaluating expr.nix
trace: Evaluating expr.nix
trace: Evaluating expr.nix
trace: Evaluating expr.nix
{ nested = <CODE>; }
$ nix-instantiate --eval normalImport.nix -A nested.nested.nested
trace: Evaluating expr.nix
{ nested = «repeated»; }

This will automatically become impossible once we fix the problem of being able to shadow builtins, so it's not a problem with scopedImport specifically.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the builtins.scopedImport behavior described in the issue and reproduce the nix-instantiate examples for caching and shadowed builtins. Read the related issue #6228 for context on function-application caching. Work is complete only after a decided direction for scopedImport is implemented or its semantics are documented and covered by appropriate tests.

Written by the indexing model from the issue text.

Assessment

Domain
tooling
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.