From b48717d1eb9d4d9d60f3460274e7d9a961a402df Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Sat, 12 Oct 2019 21:18:53 +0200 Subject: [PATCH] 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. --- lib/types.nix | 24 ++++++++++++++++++ nixos/doc/manual/development/option-types.xml | 25 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/lib/types.nix b/lib/types.nix index f406cb9204b7..e86f6d364761 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 ++ [""]); + 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 e4f8396a4bdc..55d9c123e3f1 100644 --- a/nixos/doc/manual/development/option-types.xml +++ b/nixos/doc/manual/development/option-types.xml @@ -360,6 +360,31 @@ + + + types.lazyAttrsOf t + + + + An attribute set of where all the values are of + t type. Multiple definitions result in the + joined attribute set. This is the lazy version of types.attrsOf + , allowing attributes to depend on each other. + + This version does not fully support conditional definitions! With an + option foo of this type and a definition + foo.attr = lib.mkIf false 10, evaluating + foo ? attr will return true + even though it should be false. Accessing the value will then throw + an error. For types t that have an + emptyValue defined, that value will be returned + instead of throwing an error. So if the type of foo.attr + was lazyAttrsOf (nullOr int), null + would be returned instead for the same mkIf false definition. + + + + types.loaOf t