1{ lib }:
2# Operations on attribute sets.
3
4let
5 inherit (builtins) head tail length;
6 inherit (lib.trivial) flip id mergeAttrs pipe;
7 inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName;
8 inherit (lib.lists) foldr foldl' concatMap concatLists elemAt all partition groupBy take foldl;
9in
10
11rec {
12 inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr;
13
14
15 /* Return an attribute from nested attribute sets.
16
17 Example:
18 x = { a = { b = 3; }; }
19 attrByPath ["a" "b"] 6 x
20 => 3
21 attrByPath ["z" "z"] 6 x
22 => 6
23 */
24 attrByPath = attrPath: default: e:
25 let attr = head attrPath;
26 in
27 if attrPath == [] then e
28 else if e ? ${attr}
29 then attrByPath (tail attrPath) default e.${attr}
30 else default;
31
32 /* Return if an attribute from nested attribute set exists.
33
34 Example:
35 x = { a = { b = 3; }; }
36 hasAttrByPath ["a" "b"] x
37 => true
38 hasAttrByPath ["z" "z"] x
39 => false
40
41 */
42 hasAttrByPath = attrPath: e:
43 let attr = head attrPath;
44 in
45 if attrPath == [] then true
46 else if e ? ${attr}
47 then hasAttrByPath (tail attrPath) e.${attr}
48 else false;
49
50
51 /* Return nested attribute set in which an attribute is set.
52
53 Example:
54 setAttrByPath ["a" "b"] 3
55 => { a = { b = 3; }; }
56 */
57 setAttrByPath = attrPath: value:
58 let
59 len = length attrPath;
60 atDepth = n:
61 if n == len
62 then value
63 else { ${elemAt attrPath n} = atDepth (n + 1); };
64 in atDepth 0;
65
66 /* Like `attrByPath' without a default value. If it doesn't find the
67 path it will throw.
68
69 Example:
70 x = { a = { b = 3; }; }
71 getAttrFromPath ["a" "b"] x
72 => 3
73 getAttrFromPath ["z" "z"] x
74 => error: cannot find attribute `z.z'
75 */
76 getAttrFromPath = attrPath:
77 let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
78 in attrByPath attrPath (abort errorMsg);
79
80 /* Map each attribute in the given set and merge them into a new attribute set.
81
82 Type:
83 concatMapAttrs ::
84 (String -> a -> AttrSet)
85 -> AttrSet
86 -> AttrSet
87
88 Example:
89 concatMapAttrs
90 (name: value: {
91 ${name} = value;
92 ${name + value} = value;
93 })
94 { x = "a"; y = "b"; }
95 => { x = "a"; xa = "a"; y = "b"; yb = "b"; }
96 */
97 concatMapAttrs = f: flip pipe [ (mapAttrs f) attrValues (foldl' mergeAttrs { }) ];
98
99
100 /* Update or set specific paths of an attribute set.
101
102 Takes a list of updates to apply and an attribute set to apply them to,
103 and returns the attribute set with the updates applied. Updates are
104 represented as { path = ...; update = ...; } values, where `path` is a
105 list of strings representing the attribute path that should be updated,
106 and `update` is a function that takes the old value at that attribute path
107 as an argument and returns the new
108 value it should be.
109
110 Properties:
111 - Updates to deeper attribute paths are applied before updates to more
112 shallow attribute paths
113 - Multiple updates to the same attribute path are applied in the order
114 they appear in the update list
115 - If any but the last `path` element leads into a value that is not an
116 attribute set, an error is thrown
117 - If there is an update for an attribute path that doesn't exist,
118 accessing the argument in the update function causes an error, but
119 intermediate attribute sets are implicitly created as needed
120
121 Example:
122 updateManyAttrsByPath [
123 {
124 path = [ "a" "b" ];
125 update = old: { d = old.c; };
126 }
127 {
128 path = [ "a" "b" "c" ];
129 update = old: old + 1;
130 }
131 {
132 path = [ "x" "y" ];
133 update = old: "xy";
134 }
135 ] { a.b.c = 0; }
136 => { a = { b = { d = 1; }; }; x = { y = "xy"; }; }
137 */
138 updateManyAttrsByPath = let
139 # When recursing into attributes, instead of updating the `path` of each
140 # update using `tail`, which needs to allocate an entirely new list,
141 # we just pass a prefix length to use and make sure to only look at the
142 # path without the prefix length, so that we can reuse the original list
143 # entries.
144 go = prefixLength: hasValue: value: updates:
145 let
146 # Splits updates into ones on this level (split.right)
147 # And ones on levels further down (split.wrong)
148 split = partition (el: length el.path == prefixLength) updates;
149
150 # Groups updates on further down levels into the attributes they modify
151 nested = groupBy (el: elemAt el.path prefixLength) split.wrong;
152
153 # Applies only nested modification to the input value
154 withNestedMods =
155 # Return the value directly if we don't have any nested modifications
156 if split.wrong == [] then
157 if hasValue then value
158 else
159 # Throw an error if there is no value. This `head` call here is
160 # safe, but only in this branch since `go` could only be called
161 # with `hasValue == false` for nested updates, in which case
162 # it's also always called with at least one update
163 let updatePath = (head split.right).path; in
164 throw
165 ( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' does "
166 + "not exist in the given value, but the first update to this "
167 + "path tries to access the existing value.")
168 else
169 # If there are nested modifications, try to apply them to the value
170 if ! hasValue then
171 # But if we don't have a value, just use an empty attribute set
172 # as the value, but simplify the code a bit
173 mapAttrs (name: go (prefixLength + 1) false null) nested
174 else if isAttrs value then
175 # If we do have a value and it's an attribute set, override it
176 # with the nested modifications
177 value //
178 mapAttrs (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested
179 else
180 # However if it's not an attribute set, we can't apply the nested
181 # modifications, throw an error
182 let updatePath = (head split.wrong).path; in
183 throw
184 ( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' needs to "
185 + "be updated, but path '${showAttrPath (take prefixLength updatePath)}' "
186 + "of the given value is not an attribute set, so we can't "
187 + "update an attribute inside of it.");
188
189 # We get the final result by applying all the updates on this level
190 # after having applied all the nested updates
191 # We use foldl instead of foldl' so that in case of multiple updates,
192 # intermediate values aren't evaluated if not needed
193 in foldl (acc: el: el.update acc) withNestedMods split.right;
194
195 in updates: value: go 0 true value updates;
196
197 /* Return the specified attributes from a set.
198
199 Example:
200 attrVals ["a" "b" "c"] as
201 => [as.a as.b as.c]
202 */
203 attrVals = nameList: set: map (x: set.${x}) nameList;
204
205
206 /* Return the values of all attributes in the given set, sorted by
207 attribute name.
208
209 Example:
210 attrValues {c = 3; a = 1; b = 2;}
211 => [1 2 3]
212 */
213 attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs);
214
215
216 /* Given a set of attribute names, return the set of the corresponding
217 attributes from the given set.
218
219 Example:
220 getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; }
221 => { a = 1; b = 2; }
222 */
223 getAttrs = names: attrs: genAttrs names (name: attrs.${name});
224
225 /* Collect each attribute named `attr' from a list of attribute
226 sets. Sets that don't contain the named attribute are ignored.
227
228 Example:
229 catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}]
230 => [1 2]
231 */
232 catAttrs = builtins.catAttrs or
233 (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l));
234
235
236 /* Filter an attribute set by removing all attributes for which the
237 given predicate return false.
238
239 Example:
240 filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; }
241 => { foo = 1; }
242 */
243 filterAttrs = pred: set:
244 listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
245
246
247 /* Filter an attribute set recursively by removing all attributes for
248 which the given predicate return false.
249
250 Example:
251 filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; }
252 => { foo = {}; }
253 */
254 filterAttrsRecursive = pred: set:
255 listToAttrs (
256 concatMap (name:
257 let v = set.${name}; in
258 if pred name v then [
259 (nameValuePair name (
260 if isAttrs v then filterAttrsRecursive pred v
261 else v
262 ))
263 ] else []
264 ) (attrNames set)
265 );
266
267 /* Apply fold functions to values grouped by key.
268
269 Example:
270 foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }]
271 => { a = [ 2 3 ]; }
272 */
273 foldAttrs = op: nul:
274 foldr (n: a:
275 foldr (name: o:
276 o // { ${name} = op n.${name} (a.${name} or nul); }
277 ) a (attrNames n)
278 ) {};
279
280
281 /* Recursively collect sets that verify a given predicate named `pred'
282 from the set `attrs'. The recursion is stopped when the predicate is
283 verified.
284
285 Type:
286 collect ::
287 (AttrSet -> Bool) -> AttrSet -> [x]
288
289 Example:
290 collect isList { a = { b = ["b"]; }; c = [1]; }
291 => [["b"] [1]]
292
293 collect (x: x ? outPath)
294 { a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
295 => [{ outPath = "a/"; } { outPath = "b/"; }]
296 */
297 collect = pred: attrs:
298 if pred attrs then
299 [ attrs ]
300 else if isAttrs attrs then
301 concatMap (collect pred) (attrValues attrs)
302 else
303 [];
304
305 /* Return the cartesian product of attribute set value combinations.
306
307 Example:
308 cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
309 => [
310 { a = 1; b = 10; }
311 { a = 1; b = 20; }
312 { a = 2; b = 10; }
313 { a = 2; b = 20; }
314 ]
315 */
316 cartesianProductOfSets = attrsOfLists:
317 foldl' (listOfAttrs: attrName:
318 concatMap (attrs:
319 map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
320 ) listOfAttrs
321 ) [{}] (attrNames attrsOfLists);
322
323
324 /* Utility function that creates a {name, value} pair as expected by
325 builtins.listToAttrs.
326
327 Example:
328 nameValuePair "some" 6
329 => { name = "some"; value = 6; }
330 */
331 nameValuePair = name: value: { inherit name value; };
332
333
334 /* Apply a function to each element in an attribute set. The
335 function takes two arguments --- the attribute name and its value
336 --- and returns the new value for the attribute. The result is a
337 new attribute set.
338
339 Example:
340 mapAttrs (name: value: name + "-" + value)
341 { x = "foo"; y = "bar"; }
342 => { x = "x-foo"; y = "y-bar"; }
343 */
344 mapAttrs = builtins.mapAttrs or
345 (f: set:
346 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)));
347
348
349 /* Like `mapAttrs', but allows the name of each attribute to be
350 changed in addition to the value. The applied function should
351 return both the new name and value as a `nameValuePair'.
352
353 Example:
354 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
355 { x = "a"; y = "b"; }
356 => { foo_x = "bar-a"; foo_y = "bar-b"; }
357 */
358 mapAttrs' = f: set:
359 listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
360
361
362 /* Call a function for each attribute in the given set and return
363 the result in a list.
364
365 Type:
366 mapAttrsToList ::
367 (String -> a -> b) -> AttrSet -> [b]
368
369 Example:
370 mapAttrsToList (name: value: name + value)
371 { x = "a"; y = "b"; }
372 => [ "xa" "yb" ]
373 */
374 mapAttrsToList = f: attrs:
375 map (name: f name attrs.${name}) (attrNames attrs);
376
377
378 /* Like `mapAttrs', except that it recursively applies itself to
379 attribute sets. Also, the first argument of the argument
380 function is a *list* of the names of the containing attributes.
381
382 Type:
383 mapAttrsRecursive ::
384 ([String] -> a -> b) -> AttrSet -> AttrSet
385
386 Example:
387 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value]))
388 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; }
389 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; }
390 */
391 mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
392
393
394 /* Like `mapAttrsRecursive', but it takes an additional predicate
395 function that tells it whether to recurse into an attribute
396 set. If it returns false, `mapAttrsRecursiveCond' does not
397 recurse, but does apply the map function. If it returns true, it
398 does recurse, and does not apply the map function.
399
400 Type:
401 mapAttrsRecursiveCond ::
402 (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet
403
404 Example:
405 # To prevent recursing into derivations (which are attribute
406 # sets with the attribute "type" equal to "derivation"):
407 mapAttrsRecursiveCond
408 (as: !(as ? "type" && as.type == "derivation"))
409 (x: ... do something ...)
410 attrs
411 */
412 mapAttrsRecursiveCond = cond: f: set:
413 let
414 recurse = path:
415 let
416 g =
417 name: value:
418 if isAttrs value && cond value
419 then recurse (path ++ [name]) value
420 else f (path ++ [name]) value;
421 in mapAttrs g;
422 in recurse [] set;
423
424
425 /* Generate an attribute set by mapping a function over a list of
426 attribute names.
427
428 Example:
429 genAttrs [ "foo" "bar" ] (name: "x_" + name)
430 => { foo = "x_foo"; bar = "x_bar"; }
431 */
432 genAttrs = names: f:
433 listToAttrs (map (n: nameValuePair n (f n)) names);
434
435
436 /* Check whether the argument is a derivation. Any set with
437 { type = "derivation"; } counts as a derivation.
438
439 Example:
440 nixpkgs = import <nixpkgs> {}
441 isDerivation nixpkgs.ruby
442 => true
443 isDerivation "foobar"
444 => false
445 */
446 isDerivation = x: x.type or null == "derivation";
447
448 /* Converts a store path to a fake derivation. */
449 toDerivation = path:
450 let
451 path' = builtins.storePath path;
452 res =
453 { type = "derivation";
454 name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path'));
455 outPath = path';
456 outputs = [ "out" ];
457 out = res;
458 outputName = "out";
459 };
460 in res;
461
462
463 /* If `cond' is true, return the attribute set `as',
464 otherwise an empty attribute set.
465
466 Example:
467 optionalAttrs (true) { my = "set"; }
468 => { my = "set"; }
469 optionalAttrs (false) { my = "set"; }
470 => { }
471 */
472 optionalAttrs = cond: as: if cond then as else {};
473
474
475 /* Merge sets of attributes and use the function f to merge attributes
476 values.
477
478 Example:
479 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}]
480 => { a = ["x" "y"]; }
481 */
482 zipAttrsWithNames = names: f: sets:
483 listToAttrs (map (name: {
484 inherit name;
485 value = f name (catAttrs name sets);
486 }) names);
487
488 /* Implementation note: Common names appear multiple times in the list of
489 names, hopefully this does not affect the system because the maximal
490 laziness avoid computing twice the same expression and listToAttrs does
491 not care about duplicated attribute names.
492
493 Example:
494 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}]
495 => { a = ["x" "y"]; b = ["z"] }
496 */
497 zipAttrsWith =
498 builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets);
499 /* Like `zipAttrsWith' with `(name: values: values)' as the function.
500
501 Example:
502 zipAttrs [{a = "x";} {a = "y"; b = "z";}]
503 => { a = ["x" "y"]; b = ["z"] }
504 */
505 zipAttrs = zipAttrsWith (name: values: values);
506
507 /* Does the same as the update operator '//' except that attributes are
508 merged until the given predicate is verified. The predicate should
509 accept 3 arguments which are the path to reach the attribute, a part of
510 the first attribute set and a part of the second attribute set. When
511 the predicate is verified, the value of the first attribute set is
512 replaced by the value of the second attribute set.
513
514 Example:
515 recursiveUpdateUntil (path: l: r: path == ["foo"]) {
516 # first attribute set
517 foo.bar = 1;
518 foo.baz = 2;
519 bar = 3;
520 } {
521 #second attribute set
522 foo.bar = 1;
523 foo.quz = 2;
524 baz = 4;
525 }
526
527 returns: {
528 foo.bar = 1; # 'foo.*' from the second set
529 foo.quz = 2; #
530 bar = 3; # 'bar' from the first set
531 baz = 4; # 'baz' from the second set
532 }
533
534 */
535 recursiveUpdateUntil = pred: lhs: rhs:
536 let f = attrPath:
537 zipAttrsWith (n: values:
538 let here = attrPath ++ [n]; in
539 if length values == 1
540 || pred here (elemAt values 1) (head values) then
541 head values
542 else
543 f here values
544 );
545 in f [] [rhs lhs];
546
547 /* A recursive variant of the update operator ‘//’. The recursion
548 stops when one of the attribute values is not an attribute set,
549 in which case the right hand side value takes precedence over the
550 left hand side value.
551
552 Example:
553 recursiveUpdate {
554 boot.loader.grub.enable = true;
555 boot.loader.grub.device = "/dev/hda";
556 } {
557 boot.loader.grub.device = "";
558 }
559
560 returns: {
561 boot.loader.grub.enable = true;
562 boot.loader.grub.device = "";
563 }
564
565 */
566 recursiveUpdate = recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs));
567
568 /* Returns true if the pattern is contained in the set. False otherwise.
569
570 Example:
571 matchAttrs { cpu = {}; } { cpu = { bits = 64; }; }
572 => true
573 */
574 matchAttrs = pattern: attrs: assert isAttrs pattern;
575 all id (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
576 let pat = head values; val = elemAt values 1; in
577 if length values == 1 then false
578 else if isAttrs pat then isAttrs val && matchAttrs pat val
579 else pat == val
580 ) [pattern attrs]));
581
582 /* Override only the attributes that are already present in the old set
583 useful for deep-overriding.
584
585 Example:
586 overrideExisting {} { a = 1; }
587 => {}
588 overrideExisting { b = 2; } { a = 1; }
589 => { b = 2; }
590 overrideExisting { a = 3; b = 2; } { a = 1; }
591 => { a = 1; b = 2; }
592 */
593 overrideExisting = old: new:
594 mapAttrs (name: value: new.${name} or value) old;
595
596 /* Turns a list of strings into a human-readable description of those
597 strings represented as an attribute path. The result of this function is
598 not intended to be machine-readable.
599
600 Example:
601 showAttrPath [ "foo" "10" "bar" ]
602 => "foo.\"10\".bar"
603 showAttrPath []
604 => "<root attribute path>"
605 */
606 showAttrPath = path:
607 if path == [] then "<root attribute path>"
608 else concatMapStringsSep "." escapeNixIdentifier path;
609
610 /* Get a package output.
611 If no output is found, fallback to `.out` and then to the default.
612
613 Example:
614 getOutput "dev" pkgs.openssl
615 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev"
616 */
617 getOutput = output: pkg:
618 if ! pkg ? outputSpecified || ! pkg.outputSpecified
619 then pkg.${output} or pkg.out or pkg
620 else pkg;
621
622 getBin = getOutput "bin";
623 getLib = getOutput "lib";
624 getDev = getOutput "dev";
625 getMan = getOutput "man";
626
627 /* Pick the outputs of packages to place in buildInputs */
628 chooseDevOutputs = builtins.map getDev;
629
630 /* Make various Nix tools consider the contents of the resulting
631 attribute set when looking for what to build, find, etc.
632
633 This function only affects a single attribute set; it does not
634 apply itself recursively for nested attribute sets.
635 */
636 recurseIntoAttrs =
637 attrs: attrs // { recurseForDerivations = true; };
638
639 /* Undo the effect of recurseIntoAttrs.
640 */
641 dontRecurseIntoAttrs =
642 attrs: attrs // { recurseForDerivations = false; };
643
644 /* `unionOfDisjoint x y` is equal to `x // y // z` where the
645 attrnames in `z` are the intersection of the attrnames in `x` and
646 `y`, and all values `assert` with an error message. This
647 operator is commutative, unlike (//). */
648 unionOfDisjoint = x: y:
649 let
650 intersection = builtins.intersectAttrs x y;
651 collisions = lib.concatStringsSep " " (builtins.attrNames intersection);
652 mask = builtins.mapAttrs (name: value: builtins.throw
653 "unionOfDisjoint: collision on ${name}; complete list: ${collisions}")
654 intersection;
655 in
656 (x // y) // mask;
657
658 /*** deprecated stuff ***/
659
660 zipWithNames = zipAttrsWithNames;
661 zip = builtins.trace
662 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
663}