about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2014-05-26T11·46+0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2014-05-26T12·26+0200
commitc273c15cb13bb86420dda1e5341a4e19517532b5 (patch)
tree38b670d6fbd64735502fd60525a625c18d8372ad /tests
parentf0fdbd0897ce63c138ec663ed89a94709a8441a7 (diff)
Add primop ‘scopedImport’
‘scopedImport’ works like ‘import’, except that it takes a set of
attributes to be added to the lexical scope of the expression,
essentially extending or overriding the builtin variables.  For
instance, the expression

  scopedImport { x = 1; } ./foo.nix

where foo.nix contains ‘x’, will evaluate to 1.

This has a few applications:

* It allows getting rid of function argument specifications in package
  expressions. For instance, a package expression like:

    { stdenv, fetchurl, libfoo }:

    stdenv.mkDerivation { ... buildInputs = [ libfoo ]; }

  can now we written as just

    stdenv.mkDerivation { ... buildInputs = [ libfoo ]; }

  and imported in all-packages.nix as:

    bar = scopedImport pkgs ./bar.nix;

  So whereas we once had dependencies listed in three places
  (buildInputs, the function, and the call site), they now only need
  to appear in one place.

* It allows overriding builtin functions. For instance, to trace all
  calls to ‘map’:

  let
    overrides = {
      map = f: xs: builtins.trace "map called!" (map f xs);

      # Ensure that our override gets propagated by calls to
      # import/scopedImport.
      import = fn: scopedImport overrides fn;

      scopedImport = attrs: fn: scopedImport (overrides // attrs) fn;

      # Also update ‘builtins’.
      builtins = builtins // overrides;
    };
  in scopedImport overrides ./bla.nix

* Similarly, it allows extending the set of builtin functions. For
  instance, during Nixpkgs/NixOS evaluation, the Nixpkgs library
  functions could be added to the default scope.

There is a downside: calls to scopedImport are not memoized, unlike
import. So importing a file multiple times leads to multiple parsings
/ evaluations. It would be possible to construct the AST only once,
but that would require careful handling of variables/environments.
Diffstat (limited to 'tests')
-rw-r--r--tests/lang/eval-okay-import.exp1
-rw-r--r--tests/lang/eval-okay-import.nix11
-rw-r--r--tests/lang/imported.nix3
-rw-r--r--tests/lang/imported2.nix1
4 files changed, 16 insertions, 0 deletions
diff --git a/tests/lang/eval-okay-import.exp b/tests/lang/eval-okay-import.exp
new file mode 100644
index 000000000000..c508125b55be
--- /dev/null
+++ b/tests/lang/eval-okay-import.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 5 6 7 8 9 10 ]
diff --git a/tests/lang/eval-okay-import.nix b/tests/lang/eval-okay-import.nix
new file mode 100644
index 000000000000..0b18d9413122
--- /dev/null
+++ b/tests/lang/eval-okay-import.nix
@@ -0,0 +1,11 @@
+let
+
+  overrides = {
+    import = fn: scopedImport overrides fn;
+
+    scopedImport = attrs: fn: scopedImport (overrides // attrs) fn;
+
+    builtins = builtins // overrides;
+  } // import ./lib.nix;
+
+in scopedImport overrides ./imported.nix
diff --git a/tests/lang/imported.nix b/tests/lang/imported.nix
new file mode 100644
index 000000000000..fb39ee4efacd
--- /dev/null
+++ b/tests/lang/imported.nix
@@ -0,0 +1,3 @@
+# The function ‘range’ comes from lib.nix and was added to the lexical
+# scope by scopedImport.
+range 1 5 ++ import ./imported2.nix
diff --git a/tests/lang/imported2.nix b/tests/lang/imported2.nix
new file mode 100644
index 000000000000..6d0a2992b732
--- /dev/null
+++ b/tests/lang/imported2.nix
@@ -0,0 +1 @@
+range 6 10