lib/attrsets: document all the functions

This commit is contained in:
zimbatm 2016-02-28 21:49:42 +00:00
parent a086858ef5
commit 523e328318
1 changed files with 109 additions and 28 deletions

View File

@ -12,9 +12,15 @@ rec {
inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr; inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr;
/* Return an attribute from nested attribute sets. For instance /* Return an attribute from nested attribute sets.
["x" "y"] applied to some set e returns e.x.y, if it exists. The
default value is returned otherwise. */ Example:
x = { a = { b = 3; }; }
attrByPath ["a" "b"] 6 x
=> 3
attrByPath ["z" "z"] 6 x
=> 6
*/
attrByPath = attrPath: default: e: attrByPath = attrPath: default: e:
let attr = head attrPath; let attr = head attrPath;
in in
@ -24,8 +30,15 @@ rec {
else default; else default;
/* Return if an attribute from nested attribute set exists. /* Return if an attribute from nested attribute set exists.
For instance ["x" "y"] applied to some set e returns true, if e.x.y exists. False
is returned otherwise. */ Example:
x = { a = { b = 3; }; }
hasAttrByPath ["a" "b"] x
=> true
hasAttrByPath ["z" "z"] x
=> false
*/
hasAttrByPath = attrPath: e: hasAttrByPath = attrPath: e:
let attr = head attrPath; let attr = head attrPath;
in in
@ -35,14 +48,28 @@ rec {
else false; else false;
/* Return nested attribute set in which an attribute is set. For instance /* Return nested attribute set in which an attribute is set.
["x" "y"] applied with some value v returns `x.y = v;' */
Example:
setAttrByPath ["a" "b"] 3
=> { a = { b = 3; }; }
*/
setAttrByPath = attrPath: value: setAttrByPath = attrPath: value:
if attrPath == [] then value if attrPath == [] then value
else listToAttrs else listToAttrs
[ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ]; [ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ];
/* Like `getAttrPath' without a default value. If it doesn't find the
path it will throw.
Example:
x = { a = { b = 3; }; }
getAttrFromPath ["a" "b"] x
=> 3
getAttrFromPath ["z" "z"] x
=> error: cannot find attribute `z.z'
*/
getAttrFromPath = attrPath: set: getAttrFromPath = attrPath: set:
let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'"; let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
in attrByPath attrPath (abort errorMsg) set; in attrByPath attrPath (abort errorMsg) set;
@ -109,7 +136,9 @@ rec {
) (attrNames set) ) (attrNames set)
); );
/* foldAttrs: apply fold functions to values grouped by key. Eg accumulate values as list: /* Apply fold functions to values grouped by key.
Example:
foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }] foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
=> { a = [ 2 3 ]; } => { a = [ 2 3 ]; }
*/ */
@ -147,7 +176,12 @@ rec {
/* Utility function that creates a {name, value} pair as expected by /* Utility function that creates a {name, value} pair as expected by
builtins.listToAttrs. */ builtins.listToAttrs.
Example:
nameValuePair "some" 6
=> { name = "some"; value = 6; }
*/
nameValuePair = name: value: { inherit name value; }; nameValuePair = name: value: { inherit name value; };
@ -248,11 +282,19 @@ rec {
listToAttrs (map (n: nameValuePair n (f n)) names); listToAttrs (map (n: nameValuePair n (f n)) names);
/* Check whether the argument is a derivation. */ /* Check whether the argument is a derivation. Any set with
{ type = "derivation"; } counts as a derivation.
Example:
nixpkgs = import <nixpkgs> {}
isDerivation nixpkgs.ruby
=> true
isDerivation "foobar"
=> false
*/
isDerivation = x: isAttrs x && x ? type && x.type == "derivation"; isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
/* Converts a store path to a fake derivation. */
/* Convert a store path to a fake derivation. */
toDerivation = path: toDerivation = path:
let path' = builtins.storePath path; in let path' = builtins.storePath path; in
{ type = "derivation"; { type = "derivation";
@ -262,32 +304,49 @@ rec {
}; };
/* If the Boolean `cond' is true, return the attribute set `as', /* If `cond' is true, return the attribute set `as',
otherwise an empty attribute set. */ otherwise an empty attribute set.
Example:
optionalAttrs (true) { my = "set"; }
=> { my = "set"; }
optionalAttrs (false) { my = "set"; }
=> { }
*/
optionalAttrs = cond: as: if cond then as else {}; optionalAttrs = cond: as: if cond then as else {};
/* Merge sets of attributes and use the function f to merge attributes /* Merge sets of attributes and use the function f to merge attributes
values. */ values.
Example:
zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}]
=> { a = ["x" "y"]; }
*/
zipAttrsWithNames = names: f: sets: zipAttrsWithNames = names: f: sets:
listToAttrs (map (name: { listToAttrs (map (name: {
inherit name; inherit name;
value = f name (catAttrs name sets); value = f name (catAttrs name sets);
}) names); }) names);
# implentation note: Common names appear multiple times in the list of /* Implentation note: Common names appear multiple times in the list of
# names, hopefully this does not affect the system because the maximal names, hopefully this does not affect the system because the maximal
# laziness avoid computing twice the same expression and listToAttrs does laziness avoid computing twice the same expression and listToAttrs does
# not care about duplicated attribute names. not care about duplicated attribute names.
Example:
zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}]
=> { a = ["x" "y"]; b = ["z"] }
*/
zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets; zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets;
/* Like `zipAttrsWith' with `(name: values: value)' as the function.
Example:
zipAttrs [{a = "x";} {a = "y"; b = "z";}]
=> { a = ["x" "y"]; b = ["z"] }
*/
zipAttrs = zipAttrsWith (name: values: values); zipAttrs = zipAttrsWith (name: values: values);
/* backward compatibility */
zipWithNames = zipAttrsWithNames;
zip = builtins.trace "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
/* Does the same as the update operator '//' except that attributes are /* Does the same as the update operator '//' except that attributes are
merged until the given pedicate is verified. The predicate should merged until the given pedicate is verified. The predicate should
accept 3 arguments which are the path to reach the attribute, a part of accept 3 arguments which are the path to reach the attribute, a part of
@ -351,6 +410,15 @@ rec {
!(isAttrs lhs && isAttrs rhs) !(isAttrs lhs && isAttrs rhs)
) lhs rhs; ) lhs rhs;
/* Returns true if the pattern is contained in the set. False otherwise.
FIXME(zimbatm): this example doesn't work !!!
Example:
sys = mkSystem { }
matchAttrs { cpu = { bits = 64; }; } sys
=> true
*/
matchAttrs = pattern: attrs: matchAttrs = pattern: attrs:
fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values: fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
let pat = head values; val = head (tail values); in let pat = head values; val = head (tail values); in
@ -359,10 +427,23 @@ rec {
else pat == val else pat == val
) [pattern attrs])); ) [pattern attrs]));
# override only the attributes that are already present in the old set /* Override only the attributes that are already present in the old set
# useful for deep-overriding useful for deep-overriding.
Example:
x = { a = { b = 4; c = 3; }; }
overrideExisting x { a = { b = 6; d = 2; }; }
=> { a = { b = 6; d = 2; }; }
*/
overrideExisting = old: new: overrideExisting = old: new:
old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old)); old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old));
/*** deprecated stuff ***/
deepSeqAttrs = throw "removed 2016-02-29 because unused and broken"; deepSeqAttrs = throw "removed 2016-02-29 because unused and broken";
zipWithNames = zipAttrsWithNames;
zip = builtins.trace
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
} }