lol

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.

authored by

Silvan Mosberger and committed by
Silvan Mosberger
b48717d1 4268b4f9

+49
+24
lib/types.nix
··· 302 302 functor = (defaultFunctor name) // { wrapped = elemType; }; 303 303 }; 304 304 305 + # A version of attrsOf that's lazy in its values at the expense of 306 + # conditional definitions not working properly. E.g. defining a value with 307 + # `foo.attr = mkIf false 10`, then `foo ? attr == true`, whereas with 308 + # attrsOf it would correctly be `false`. Accessing `foo.attr` would throw an 309 + # error that it's not defined. Use only if conditional definitions don't make sense. 310 + lazyAttrsOf = elemType: mkOptionType rec { 311 + name = "lazyAttrsOf"; 312 + description = "lazy attribute set of ${elemType.description}s"; 313 + check = isAttrs; 314 + merge = loc: defs: 315 + zipAttrsWith (name: defs: 316 + let merged = mergeDefinitions (loc ++ [name]) elemType defs; 317 + # mergedValue will trigger an appropriate error when accessed 318 + in merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue 319 + ) 320 + # Push down position info. 321 + (map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs); 322 + emptyValue = { value = {}; }; 323 + getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]); 324 + getSubModules = elemType.getSubModules; 325 + substSubModules = m: lazyAttrsOf (elemType.substSubModules m); 326 + functor = (defaultFunctor name) // { wrapped = elemType; }; 327 + }; 328 + 305 329 # List or attribute set of ... 306 330 loaOf = elemType: 307 331 let
+25
nixos/doc/manual/development/option-types.xml
··· 362 362 </varlistentry> 363 363 <varlistentry> 364 364 <term> 365 + <varname>types.lazyAttrsOf</varname> <replaceable>t</replaceable> 366 + </term> 367 + <listitem> 368 + <para> 369 + An attribute set of where all the values are of 370 + <replaceable>t</replaceable> type. Multiple definitions result in the 371 + joined attribute set. This is the lazy version of <varname>types.attrsOf 372 + </varname>, allowing attributes to depend on each other. 373 + <warning><para> 374 + This version does not fully support conditional definitions! With an 375 + option <varname>foo</varname> of this type and a definition 376 + <literal>foo.attr = lib.mkIf false 10</literal>, evaluating 377 + <literal>foo ? attr</literal> will return <literal>true</literal> 378 + even though it should be false. Accessing the value will then throw 379 + an error. For types <replaceable>t</replaceable> that have an 380 + <literal>emptyValue</literal> defined, that value will be returned 381 + instead of throwing an error. So if the type of <literal>foo.attr</literal> 382 + was <literal>lazyAttrsOf (nullOr int)</literal>, <literal>null</literal> 383 + would be returned instead for the same <literal>mkIf false</literal> definition. 384 + </para></warning> 385 + </para> 386 + </listitem> 387 + </varlistentry> 388 + <varlistentry> 389 + <term> 365 390 <varname>types.loaOf</varname> <replaceable>t</replaceable> 366 391 </term> 367 392 <listitem>