summary refs log tree commit diff
diff options
context:
space:
mode:
authorSilvan Mosberger <infinisil@icloud.com>2019-10-12 21:18:53 +0200
committerSilvan Mosberger <contact@infinisil.com>2020-01-10 16:19:55 +0100
commitb48717d1eb9d4d9d60f3460274e7d9a961a402df (patch)
tree963ddca03bc11d61026ed701c521aa2633c65f63
parent4268b4f9cffc46482ece4a8e0128b8ef09ea6db2 (diff)
downloadnixpkgs-b48717d1eb9d4d9d60f3460274e7d9a961a402df.tar
nixpkgs-b48717d1eb9d4d9d60f3460274e7d9a961a402df.tar.gz
nixpkgs-b48717d1eb9d4d9d60f3460274e7d9a961a402df.tar.bz2
nixpkgs-b48717d1eb9d4d9d60f3460274e7d9a961a402df.tar.lz
nixpkgs-b48717d1eb9d4d9d60f3460274e7d9a961a402df.tar.xz
nixpkgs-b48717d1eb9d4d9d60f3460274e7d9a961a402df.tar.zst
nixpkgs-b48717d1eb9d4d9d60f3460274e7d9a961a402df.zip
lib/types: Introduce lazyAttrsOf
The standard attrsOf is strict in its *values*, meaning it's impossible to
access only one attribute value without evaluating all others as well.
lazyAttrsOf is a version that doesn't have that problem, at the expense
of conditional definitions not properly working anymore.
-rw-r--r--lib/types.nix24
-rw-r--r--nixos/doc/manual/development/option-types.xml25
2 files changed, 49 insertions, 0 deletions
diff --git a/lib/types.nix b/lib/types.nix
index f406cb9204b..e86f6d36476 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -302,6 +302,30 @@ rec {
       functor = (defaultFunctor name) // { wrapped = elemType; };
     };
 
+    # A version of attrsOf that's lazy in its values at the expense of
+    # conditional definitions not working properly. E.g. defining a value with
+    # `foo.attr = mkIf false 10`, then `foo ? attr == true`, whereas with
+    # attrsOf it would correctly be `false`. Accessing `foo.attr` would throw an
+    # error that it's not defined. Use only if conditional definitions don't make sense.
+    lazyAttrsOf = elemType: mkOptionType rec {
+      name = "lazyAttrsOf";
+      description = "lazy attribute set of ${elemType.description}s";
+      check = isAttrs;
+      merge = loc: defs:
+        zipAttrsWith (name: defs:
+          let merged = mergeDefinitions (loc ++ [name]) elemType defs;
+          # mergedValue will trigger an appropriate error when accessed
+          in merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue
+        )
+        # Push down position info.
+        (map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs);
+      emptyValue = { value = {}; };
+      getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
+      getSubModules = elemType.getSubModules;
+      substSubModules = m: lazyAttrsOf (elemType.substSubModules m);
+      functor = (defaultFunctor name) // { wrapped = elemType; };
+    };
+
     # List or attribute set of ...
     loaOf = elemType:
       let
diff --git a/nixos/doc/manual/development/option-types.xml b/nixos/doc/manual/development/option-types.xml
index e4f8396a4bd..55d9c123e3f 100644
--- a/nixos/doc/manual/development/option-types.xml
+++ b/nixos/doc/manual/development/option-types.xml
@@ -362,6 +362,31 @@
    </varlistentry>
    <varlistentry>
     <term>
+     <varname>types.lazyAttrsOf</varname> <replaceable>t</replaceable>
+    </term>
+    <listitem>
+     <para>
+      An attribute set of where all the values are of
+      <replaceable>t</replaceable> type. Multiple definitions result in the
+      joined attribute set. This is the lazy version of <varname>types.attrsOf
+      </varname>, allowing attributes to depend on each other.
+      <warning><para>
+       This version does not fully support conditional definitions! With an
+       option <varname>foo</varname> of this type and a definition
+       <literal>foo.attr = lib.mkIf false 10</literal>, evaluating
+       <literal>foo ? attr</literal> will return <literal>true</literal>
+       even though it should be false. Accessing the value will then throw
+       an error. For types <replaceable>t</replaceable> that have an
+       <literal>emptyValue</literal> defined, that value will be returned
+       instead of throwing an error. So if the type of <literal>foo.attr</literal>
+       was <literal>lazyAttrsOf (nullOr int)</literal>, <literal>null</literal>
+       would be returned instead for the same <literal>mkIf false</literal> definition.
+      </para></warning>
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term>
      <varname>types.loaOf</varname> <replaceable>t</replaceable>
     </term>
     <listitem>