Merge pull request #13546 from zimbatm/document-stdlib

Document stdlib
This commit is contained in:
Domen Kožar 2016-03-10 12:31:33 +00:00
commit 721462634f
3 changed files with 600 additions and 145 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));
deepSeqAttrs = x: y: deepSeqList (attrValues x) y;
/*** deprecated stuff ***/
deepSeqAttrs = throw "removed 2016-02-29 because unused and broken";
zipWithNames = zipAttrsWithNames;
zip = builtins.trace
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
} }

View File

@ -6,17 +6,26 @@ rec {
inherit (builtins) head tail length isList elemAt concatLists filter elem genList; inherit (builtins) head tail length isList elemAt concatLists filter elem genList;
/* Create a list consisting of a single element. `singleton x' is
sometimes more convenient with respect to indentation than `[x]'
when x spans multiple lines.
# Create a list consisting of a single element. `singleton x' is Example:
# sometimes more convenient with respect to indentation than `[x]' singleton "foo"
# when x spans multiple lines. => [ "foo" ]
*/
singleton = x: [x]; singleton = x: [x];
/* "Fold" a binary function `op' between successive elements of
`list' with `nul' as the starting value, i.e., `fold op nul [x_1
x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. (This is
Haskell's foldr).
# "Fold" a binary function `op' between successive elements of Example:
# `list' with `nul' as the starting value, i.e., `fold op nul [x_1 concat = fold (a: b: a + b) "z"
# x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. (This is concat [ "a" "b" "c" ]
# Haskell's foldr). => "abcnul"
*/
fold = op: nul: list: fold = op: nul: list:
let let
len = length list; len = length list;
@ -26,8 +35,14 @@ rec {
else op (elemAt list n) (fold' (n + 1)); else op (elemAt list n) (fold' (n + 1));
in fold' 0; in fold' 0;
# Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul /* Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
# x_1) x_2) ... x_n)'. x_1) x_2) ... x_n)'.
Example:
lconcat = foldl (a: b: a + b) "z"
lconcat [ "a" "b" "c" ]
=> "zabc"
*/
foldl = op: nul: list: foldl = op: nul: list:
let let
len = length list; len = length list;
@ -37,13 +52,22 @@ rec {
else op (foldl' (n - 1)) (elemAt list n); else op (foldl' (n - 1)) (elemAt list n);
in foldl' (length list - 1); in foldl' (length list - 1);
/* Strict version of foldl.
# Strict version of foldl. The difference is that evaluation is forced upon access. Usually used
with small whole results (in contract with lazily-generated list or large
lists where only a part is consumed.)
*/
foldl' = builtins.foldl' or foldl; foldl' = builtins.foldl' or foldl;
/* Map with index
# Map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] == FIXME(zimbatm): why does this start to count at 1?
# ["a-1" "b-2"]'. FIXME: why does this start to count at 1?
Example:
imap (i: v: "${v}-${toString i}") ["a" "b"]
=> [ "a-1" "b-2" ]
*/
imap = imap =
if builtins ? genList then if builtins ? genList then
f: list: genList (n: f (n + 1) (elemAt list n)) (length list) f: list: genList (n: f (n + 1) (elemAt list n)) (length list)
@ -57,73 +81,141 @@ rec {
else [ (f (n + 1) (elemAt list n)) ] ++ imap' (n + 1); else [ (f (n + 1) (elemAt list n)) ] ++ imap' (n + 1);
in imap' 0; in imap' 0;
/* Map and concatenate the result.
# Map and concatenate the result. Example:
concatMap (x: [x] ++ ["z"]) ["a" "b"]
=> [ "a" "z" "b" "z" ]
*/
concatMap = f: list: concatLists (map f list); concatMap = f: list: concatLists (map f list);
/* Flatten the argument into a single list; that is, nested lists are
spliced into the top-level lists.
# Flatten the argument into a single list; that is, nested lists are Example:
# spliced into the top-level lists. E.g., `flatten [1 [2 [3] 4] 5] flatten [1 [2 [3] 4] 5]
# == [1 2 3 4 5]' and `flatten 1 == [1]'. => [1 2 3 4 5]
flatten 1
=> [1]
*/
flatten = x: flatten = x:
if isList x if isList x
then foldl' (x: y: x ++ (flatten y)) [] x then foldl' (x: y: x ++ (flatten y)) [] x
else [x]; else [x];
/* Remove elements equal to 'e' from a list. Useful for buildInputs.
# Remove elements equal to 'e' from a list. Useful for buildInputs. Example:
remove 3 [ 1 3 4 3 ]
=> [ 1 4 ]
*/
remove = e: filter (x: x != e); remove = e: filter (x: x != e);
/* Find the sole element in the list matching the specified
predicate, returns `default' if no such element exists, or
`multiple' if there are multiple matching elements.
# Find the sole element in the list matching the specified Example:
# predicate, returns `default' if no such element exists, or findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ]
# `multiple' if there are multiple matching elements. => "multiple"
findSingle (x: x == 3) "none" "multiple" [ 1 3 ]
=> 3
findSingle (x: x == 3) "none" "multiple" [ 1 9 ]
=> "none"
*/
findSingle = pred: default: multiple: list: findSingle = pred: default: multiple: list:
let found = filter pred list; len = length found; let found = filter pred list; len = length found;
in if len == 0 then default in if len == 0 then default
else if len != 1 then multiple else if len != 1 then multiple
else head found; else head found;
/* Find the first element in the list matching the specified
predicate or returns `default' if no such element exists.
# Find the first element in the list matching the specified Example:
# predicate or returns `default' if no such element exists. findFirst (x: x > 3) 7 [ 1 6 4 ]
=> 6
findFirst (x: x > 9) 7 [ 1 6 4 ]
=> 7
*/
findFirst = pred: default: list: findFirst = pred: default: list:
let found = filter pred list; let found = filter pred list;
in if found == [] then default else head found; in if found == [] then default else head found;
/* Return true iff function `pred' returns true for at least element
of `list'.
# Return true iff function `pred' returns true for at least element Example:
# of `list'. any isString [ 1 "a" { } ]
=> true
any isString [ 1 { } ]
=> false
*/
any = builtins.any or (pred: fold (x: y: if pred x then true else y) false); any = builtins.any or (pred: fold (x: y: if pred x then true else y) false);
/* Return true iff function `pred' returns true for all elements of
`list'.
# Return true iff function `pred' returns true for all elements of Example:
# `list'. all (x: x < 3) [ 1 2 ]
=> true
all (x: x < 3) [ 1 2 3 ]
=> false
*/
all = builtins.all or (pred: fold (x: y: if pred x then y else false) true); all = builtins.all or (pred: fold (x: y: if pred x then y else false) true);
/* Count how many times function `pred' returns true for the elements
of `list'.
# Count how many times function `pred' returns true for the elements Example:
# of `list'. count (x: x == 3) [ 3 2 3 4 6 ]
=> 2
*/
count = pred: foldl' (c: x: if pred x then c + 1 else c) 0; count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
/* Return a singleton list or an empty list, depending on a boolean
value. Useful when building lists with optional elements
(e.g. `++ optional (system == "i686-linux") flashplayer').
# Return a singleton list or an empty list, depending on a boolean Example:
# value. Useful when building lists with optional elements optional true "foo"
# (e.g. `++ optional (system == "i686-linux") flashplayer'). => [ "foo" ]
optional false "foo"
=> [ ]
*/
optional = cond: elem: if cond then [elem] else []; optional = cond: elem: if cond then [elem] else [];
/* Return a list or an empty list, dependening on a boolean value.
# Return a list or an empty list, dependening on a boolean value. Example:
optionals true [ 2 3 ]
=> [ 2 3 ]
optionals false [ 2 3 ]
=> [ ]
*/
optionals = cond: elems: if cond then elems else []; optionals = cond: elems: if cond then elems else [];
# If argument is a list, return it; else, wrap it in a singleton /* If argument is a list, return it; else, wrap it in a singleton
# list. If you're using this, you should almost certainly list. If you're using this, you should almost certainly
# reconsider if there isn't a more "well-typed" approach. reconsider if there isn't a more "well-typed" approach.
Example:
toList [ 1 2 ]
=> [ 1 2 ]
toList "hi"
=> [ "hi "]
*/
toList = x: if isList x then x else [x]; toList = x: if isList x then x else [x];
/* Return a list of integers from `first' up to and including `last'.
# Return a list of integers from `first' up to and including `last'. Example:
range 2 4
=> [ 2 3 4 ]
range 3 2
=> [ ]
*/
range = range =
if builtins ? genList then if builtins ? genList then
first: last: first: last:
@ -136,9 +228,13 @@ rec {
then [] then []
else [first] ++ range (first + 1) last; else [first] ++ range (first + 1) last;
/* Splits the elements of a list in two lists, `right' and
`wrong', depending on the evaluation of a predicate.
# Partition the elements of a list in two lists, `right' and Example:
# `wrong', depending on the evaluation of a predicate. partition (x: x > 2) [ 5 1 2 3 4 ]
=> { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
*/
partition = pred: partition = pred:
fold (h: t: fold (h: t:
if pred h if pred h
@ -146,7 +242,14 @@ rec {
else { right = t.right; wrong = [h] ++ t.wrong; } else { right = t.right; wrong = [h] ++ t.wrong; }
) { right = []; wrong = []; }; ) { right = []; wrong = []; };
/* Merges two lists of the same size together. If the sizes aren't the same
the merging stops at the shortest. How both lists are merged is defined
by the first argument.
Example:
zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
=> ["he" "lo"]
*/
zipListsWith = zipListsWith =
if builtins ? genList then if builtins ? genList then
f: fst: snd: genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd)) f: fst: snd: genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd))
@ -161,21 +264,37 @@ rec {
else []; else [];
in zipListsWith' 0; in zipListsWith' 0;
/* Merges two lists of the same size together. If the sizes aren't the same
the merging stops at the shortest.
Example:
zipLists [ 1 2 ] [ "a" "b" ]
=> [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ]
*/
zipLists = zipListsWith (fst: snd: { inherit fst snd; }); zipLists = zipListsWith (fst: snd: { inherit fst snd; });
/* Reverse the order of the elements of a list.
# Reverse the order of the elements of a list. Example:
reverseList [ "b" "o" "j" ]
=> [ "j" "o" "b" ]
*/
reverseList = reverseList =
if builtins ? genList then if builtins ? genList then
xs: let l = length xs; in genList (n: elemAt xs (l - n - 1)) l xs: let l = length xs; in genList (n: elemAt xs (l - n - 1)) l
else else
fold (e: acc: acc ++ [ e ]) []; fold (e: acc: acc ++ [ e ]) [];
/* Sort a list based on a comparator function which compares two
elements and returns true if the first argument is strictly below
the second argument. The returned list is sorted in an increasing
order. The implementation does a quick-sort.
# Sort a list based on a comparator function which compares two Example:
# elements and returns true if the first argument is strictly below sort (a: b: a < b) [ 5 3 7 ]
# the second argument. The returned list is sorted in an increasing => [ 3 5 7 ]
# order. The implementation does a quick-sort. */
sort = builtins.sort or ( sort = builtins.sort or (
strictLess: list: strictLess: list:
let let
@ -193,8 +312,14 @@ rec {
if len < 2 then list if len < 2 then list
else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right)); else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
/* Return the first (at most) N elements of a list.
# Return the first (at most) N elements of a list. Example:
take 2 [ "a" "b" "c" "d" ]
=> [ "a" "b" ]
take 2 [ ]
=> [ ]
*/
take = take =
if builtins ? genList then if builtins ? genList then
count: sublist 0 count count: sublist 0 count
@ -209,8 +334,14 @@ rec {
[ (elemAt list n) ] ++ take' (n + 1); [ (elemAt list n) ] ++ take' (n + 1);
in take' 0; in take' 0;
/* Remove the first (at most) N elements of a list.
# Remove the first (at most) N elements of a list. Example:
drop 2 [ "a" "b" "c" "d" ]
=> [ "c" "d" ]
drop 2 [ ]
=> [ ]
*/
drop = drop =
if builtins ? genList then if builtins ? genList then
count: list: sublist count (length list) list count: list: sublist count (length list) list
@ -225,9 +356,15 @@ rec {
drop' (n - 1) ++ [ (elemAt list n) ]; drop' (n - 1) ++ [ (elemAt list n) ];
in drop' (len - 1); in drop' (len - 1);
/* Return a list consisting of at most count elements of list,
starting at index start.
# Return a list consisting of at most count elements of list, Example:
# starting at index start. sublist 1 3 [ "a" "b" "c" "d" "e" ]
=> [ "b" "c" "d" ]
sublist 1 3 [ ]
=> [ ]
*/
sublist = start: count: list: sublist = start: count: list:
let len = length list; in let len = length list; in
genList genList
@ -236,23 +373,36 @@ rec {
else if start + count > len then len - start else if start + count > len then len - start
else count); else count);
/* Return the last element of a list.
# Return the last element of a list. Example:
last [ 1 2 3 ]
=> 3
*/
last = list: last = list:
assert list != []; elemAt list (length list - 1); assert list != []; elemAt list (length list - 1);
/* Return all elements but the last
# Return all elements but the last Example:
init [ 1 2 3 ]
=> [ 1 2 ]
*/
init = list: assert list != []; take (length list - 1) list; init = list: assert list != []; take (length list - 1) list;
deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y; /* FIXME(zimbatm) Not used anywhere
*/
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f]; crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
# Remove duplicate elements from the list. O(n^2) complexity. /* Remove duplicate elements from the list. O(n^2) complexity.
Example:
unique [ 3 2 3 4 ]
=> [ 3 2 4 ]
*/
unique = list: unique = list:
if list == [] then if list == [] then
[] []
@ -262,12 +412,24 @@ rec {
xs = unique (drop 1 list); xs = unique (drop 1 list);
in [x] ++ remove x xs; in [x] ++ remove x xs;
/* Intersects list 'e' and another list. O(nm) complexity.
# Intersects list 'e' and another list. O(nm) complexity. Example:
intersectLists [ 1 2 3 ] [ 6 3 2 ]
=> [ 3 2 ]
*/
intersectLists = e: filter (x: elem x e); intersectLists = e: filter (x: elem x e);
/* Subtracts list 'e' from another list. O(nm) complexity.
# Subtracts list 'e' from another list. O(nm) complexity. Example:
subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ]
=> [ 1 4 5 ]
*/
subtractLists = e: filter (x: !(elem x e)); subtractLists = e: filter (x: !(elem x e));
/*** deprecated stuff ***/
deepSeqList = throw "removed 2016-02-29 because unused and broken";
} }

View File

@ -10,65 +10,147 @@ rec {
inherit (builtins) stringLength substring head tail isString replaceStrings; inherit (builtins) stringLength substring head tail isString replaceStrings;
/* Concatenate a list of strings.
# Concatenate a list of strings. Example:
concatStrings ["foo" "bar"]
=> "foobar"
*/
concatStrings = concatStrings =
if builtins ? concatStringsSep then if builtins ? concatStringsSep then
builtins.concatStringsSep "" builtins.concatStringsSep ""
else else
lib.foldl' (x: y: x + y) ""; lib.foldl' (x: y: x + y) "";
/* Map a function over a list and concatenate the resulting strings.
# Map a function over a list and concatenate the resulting strings. Example:
concatMapStrings (x: "a" + x) ["foo" "bar"]
=> "afooabar"
*/
concatMapStrings = f: list: concatStrings (map f list); concatMapStrings = f: list: concatStrings (map f list);
/* Like `concatMapStrings' except that the f functions also gets the
position as a parameter.
Example:
concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"]
=> "1-foo2-bar"
*/
concatImapStrings = f: list: concatStrings (lib.imap f list); concatImapStrings = f: list: concatStrings (lib.imap f list);
/* Place an element between each element of a list
# Place an element between each element of a list, e.g., Example:
# `intersperse "," ["a" "b" "c"]' returns ["a" "," "b" "," "c"]. intersperse "/" ["usr" "local" "bin"]
=> ["usr" "/" "local" "/" "bin"].
*/
intersperse = separator: list: intersperse = separator: list:
if list == [] || length list == 1 if list == [] || length list == 1
then list then list
else tail (lib.concatMap (x: [separator x]) list); else tail (lib.concatMap (x: [separator x]) list);
/* Concatenate a list of strings with a separator between each element
# Concatenate a list of strings with a separator between each element, e.g. Example:
# concatStringsSep " " ["foo" "bar" "xyzzy"] == "foo bar xyzzy" concatStringsSep "/" ["usr" "local" "bin"]
=> "usr/local/bin"
*/
concatStringsSep = builtins.concatStringsSep or (separator: list: concatStringsSep = builtins.concatStringsSep or (separator: list:
concatStrings (intersperse separator list)); concatStrings (intersperse separator list));
/* First maps over the list and then concatenates it.
Example:
concatMapStringsSep "-" (x: toUpper x) ["foo" "bar" "baz"]
=> "FOO-BAR-BAZ"
*/
concatMapStringsSep = sep: f: list: concatStringsSep sep (map f list); concatMapStringsSep = sep: f: list: concatStringsSep sep (map f list);
/* First imaps over the list and then concatenates it.
Example:
concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ]
=> "6-3-2"
*/
concatImapStringsSep = sep: f: list: concatStringsSep sep (lib.imap f list); concatImapStringsSep = sep: f: list: concatStringsSep sep (lib.imap f list);
/* Construct a Unix-style search path consisting of each `subDir"
directory of the given list of packages.
# Construct a Unix-style search path consisting of each `subDir" Example:
# directory of the given list of packages. For example, makeSearchPath "bin" ["/root" "/usr" "/usr/local"]
# `makeSearchPath "bin" ["x" "y" "z"]' returns "x/bin:y/bin:z/bin". => "/root/bin:/usr/bin:/usr/local/bin"
makeSearchPath "bin" ["/"]
=> "//bin"
*/
makeSearchPath = subDir: packages: makeSearchPath = subDir: packages:
concatStringsSep ":" (map (path: path + "/" + subDir) packages); concatStringsSep ":" (map (path: path + "/" + subDir) packages);
/* Construct a library search path (such as RPATH) containing the
libraries for a set of packages
# Construct a library search path (such as RPATH) containing the Example:
# libraries for a set of packages, e.g. "${pkg1}/lib:${pkg2}/lib:...". makeLibraryPath [ "/usr" "/usr/local" ]
=> "/usr/lib:/usr/local/lib"
pkgs = import <nixpkgs> { }
makeLibraryPath [ pkgs.openssl pkgs.zlib ]
=> "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib"
*/
makeLibraryPath = makeSearchPath "lib"; makeLibraryPath = makeSearchPath "lib";
# Construct a binary search path (such as $PATH) containing the /* Construct a binary search path (such as $PATH) containing the
# binaries for a set of packages, e.g. "${pkg1}/bin:${pkg2}/bin:...". binaries for a set of packages.
Example:
makeBinPath ["/root" "/usr" "/usr/local"]
=> "/root/bin:/usr/bin:/usr/local/bin"
*/
makeBinPath = makeSearchPath "bin"; makeBinPath = makeSearchPath "bin";
# Idem for Perl search paths. /* Construct a perl search path (such as $PERL5LIB)
FIXME(zimbatm): this should be moved in perl-specific code
Example:
pkgs = import <nixpkgs> { }
makePerlPath [ pkgs.perlPackages.NetSMTP ]
=> "/nix/store/n0m1fk9c960d8wlrs62sncnadygqqc6y-perl-Net-SMTP-1.25/lib/perl5/site_perl"
*/
makePerlPath = makeSearchPath "lib/perl5/site_perl"; makePerlPath = makeSearchPath "lib/perl5/site_perl";
/* Dependening on the boolean `cond', return either the given string
or the empty string. Useful to contatenate against a bigger string.
# Dependening on the boolean `cond', return either the given string Example:
# or the empty string. optionalString true "some-string"
=> "some-string"
optionalString false "some-string"
=> ""
*/
optionalString = cond: string: if cond then string else ""; optionalString = cond: string: if cond then string else "";
/* Determine whether a string has given prefix.
# Determine whether a string has given prefix/suffix. Example:
hasPrefix "foo" "foobar"
=> true
hasPrefix "foo" "barfoo"
=> false
*/
hasPrefix = pref: str: hasPrefix = pref: str:
substring 0 (stringLength pref) str == pref; substring 0 (stringLength pref) str == pref;
/* Determine whether a string has given suffix.
Example:
hasSuffix "foo" "foobar"
=> false
hasSuffix "foo" "barfoo"
=> true
*/
hasSuffix = suff: str: hasSuffix = suff: str:
let let
lenStr = stringLength str; lenStr = stringLength str;
@ -76,36 +158,55 @@ rec {
in lenStr >= lenSuff && in lenStr >= lenSuff &&
substring (lenStr - lenSuff) lenStr str == suff; substring (lenStr - lenSuff) lenStr str == suff;
/* Convert a string to a list of characters (i.e. singleton strings).
This allows you to, e.g., map a function over each character. However,
note that this will likely be horribly inefficient; Nix is not a
general purpose programming language. Complex string manipulations
should, if appropriate, be done in a derivation.
Also note that Nix treats strings as a list of bytes and thus doesn't
handle unicode.
# Convert a string to a list of characters (i.e. singleton strings). Example:
# For instance, "abc" becomes ["a" "b" "c"]. This allows you to, stringToCharacters ""
# e.g., map a function over each character. However, note that this => [ ]
# will likely be horribly inefficient; Nix is not a general purpose stringToCharacters "abc"
# programming language. Complex string manipulations should, if => [ "a" "b" "c" ]
# appropriate, be done in a derivation. stringToCharacters "💩"
=> [ "<EFBFBD>" "<EFBFBD>" "<EFBFBD>" "<EFBFBD>" ]
*/
stringToCharacters = s: stringToCharacters = s:
map (p: substring p 1 s) (lib.range 0 (stringLength s - 1)); map (p: substring p 1 s) (lib.range 0 (stringLength s - 1));
/* Manipulate a string character by character and replace them by
strings before concatenating the results.
# Manipulate a string charactter by character and replace them by Example:
# strings before concatenating the results. stringAsChars (x: if x == "a" then "i" else x) "nax"
=> "nix"
*/
stringAsChars = f: s: stringAsChars = f: s:
concatStrings ( concatStrings (
map f (stringToCharacters s) map f (stringToCharacters s)
); );
/* Escape occurrence of the elements of list in string by
prefixing it with a backslash.
# Escape occurrence of the elements of list in string by Example:
# prefixing it with a backslash. For example, escape ["(" ")"] escape ["(" ")"] "(foo)"
# "(foo)" returns the string \(foo\). => "\\(foo\\)"
*/
escape = list: replaceChars list (map (c: "\\${c}") list); escape = list: replaceChars list (map (c: "\\${c}") list);
/* Escape all characters that have special meaning in the Bourne shell.
# Escape all characters that have special meaning in the Bourne shell. Example:
escapeShellArg "so([<>])me"
=> "so\\(\\[\\<\\>\\]\\)me"
*/
escapeShellArg = lib.escape (stringToCharacters "\\ ';$`()|<>\t*[]"); escapeShellArg = lib.escape (stringToCharacters "\\ ';$`()|<>\t*[]");
/* Obsolete - use replaceStrings instead. */
# Obsolete - use replaceStrings instead.
replaceChars = builtins.replaceStrings or ( replaceChars = builtins.replaceStrings or (
del: new: s: del: new: s:
let let
@ -119,21 +220,52 @@ rec {
in in
stringAsChars subst s); stringAsChars subst s);
# Case conversion utilities. # Case conversion utilities.
lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz"; lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/* Converts an ASCII string to lower-case.
Example:
toLower "HOME"
=> "home"
*/
toLower = replaceChars upperChars lowerChars; toLower = replaceChars upperChars lowerChars;
/* Converts an ASCII string to upper-case.
Example:
toLower "home"
=> "HOME"
*/
toUpper = replaceChars lowerChars upperChars; toUpper = replaceChars lowerChars upperChars;
/* Appends string context from another string. This is an implementation
detail of Nix.
# Appends string context from another string. Strings in Nix carry an invisible `context' which is a list of strings
representing store paths. If the string is later used in a derivation
attribute, the derivation will properly populate the inputDrvs and
inputSrcs.
Example:
pkgs = import <nixpkgs> { };
addContextFrom pkgs.coreutils "bar"
=> "bar"
*/
addContextFrom = a: b: substring 0 0 a + b; addContextFrom = a: b: substring 0 0 a + b;
/* Cut a string with a separator and produces a list of strings which
were separated by this separator.
# Cut a string with a separator and produces a list of strings which NOTE: this function is not performant and should be avoided
# were separated by this separator; e.g., `splitString "."
# "foo.bar.baz"' returns ["foo" "bar" "baz"]. Example:
splitString "." "foo.bar.baz"
=> [ "foo" "bar" "baz" ]
splitString "/" "/usr/local/bin"
=> [ "" "usr" "local" "bin" ]
*/
splitString = _sep: _s: splitString = _sep: _s:
let let
sep = addContextFrom _s _sep; sep = addContextFrom _s _sep;
@ -157,10 +289,15 @@ rec {
in in
recurse 0 0; recurse 0 0;
/* Return the suffix of the second argument if the first argument matches
its prefix.
# return the suffix of the second argument if the first argument match its Example:
# prefix. e.g., removePrefix "foo." "foo.bar.baz"
# `removePrefix "foo." "foo.bar.baz"' returns "bar.baz". => "bar.baz"
removePrefix "xxx" "foo.bar.baz"
=> "foo.bar.baz"
*/
removePrefix = pre: s: removePrefix = pre: s:
let let
preLen = stringLength pre; preLen = stringLength pre;
@ -171,6 +308,15 @@ rec {
else else
s; s;
/* Return the prefix of the second argument if the first argument matches
its suffix.
Example:
removeSuffix "front" "homefront"
=> "home"
removeSuffix "xxx" "homefront"
=> "homefront"
*/
removeSuffix = suf: s: removeSuffix = suf: s:
let let
sufLen = stringLength suf; sufLen = stringLength suf;
@ -181,25 +327,49 @@ rec {
else else
s; s;
# Return true iff string v1 denotes a version older than v2. /* Return true iff string v1 denotes a version older than v2.
Example:
versionOlder "1.1" "1.2"
=> true
versionOlder "1.1" "1.1"
=> false
*/
versionOlder = v1: v2: builtins.compareVersions v2 v1 == 1; versionOlder = v1: v2: builtins.compareVersions v2 v1 == 1;
/* Return true iff string v1 denotes a version equal to or newer than v2.
# Return true iff string v1 denotes a version equal to or newer than v2. Example:
versionAtLeast "1.1" "1.0"
=> true
versionAtLeast "1.1" "1.1"
=> true
versionAtLeast "1.1" "1.2"
=> false
*/
versionAtLeast = v1: v2: !versionOlder v1 v2; versionAtLeast = v1: v2: !versionOlder v1 v2;
/* This function takes an argument that's either a derivation or a
derivation's "name" attribute and extracts the version part from that
argument.
# This function takes an argument that's either a derivation or a Example:
# derivation's "name" attribute and extracts the version part from that getVersion "youtube-dl-2016.01.01"
# argument. For example: => "2016.01.01"
# getVersion pkgs.youtube-dl
# lib.getVersion "youtube-dl-2016.01.01" ==> "2016.01.01" => "2016.01.01"
# lib.getVersion pkgs.youtube-dl ==> "2016.01.01" */
getVersion = x: (builtins.parseDrvName (x.name or x)).version; getVersion = x: (builtins.parseDrvName (x.name or x)).version;
/* Extract name with version from URL. Ask for separator which is
supposed to start extension.
# Extract name with version from URL. Ask for separator which is Example:
# supposed to start extension. nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-"
=> "nix"
nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_"
=> "nix-1.7-x86"
*/
nameFromURL = url: sep: nameFromURL = url: sep:
let let
components = splitString "/" url; components = splitString "/" url;
@ -207,14 +377,24 @@ rec {
name = builtins.head (splitString sep filename); name = builtins.head (splitString sep filename);
in assert name != filename; name; in assert name != filename; name;
/* Create an --{enable,disable}-<feat> string that can be passed to
standard GNU Autoconf scripts.
# Create an --{enable,disable}-<feat> string that can be passed to Example:
# standard GNU Autoconf scripts. enableFeature true "shared"
=> "--enable-shared"
enableFeature false "shared"
=> "--disable-shared"
*/
enableFeature = enable: feat: "--${if enable then "enable" else "disable"}-${feat}"; enableFeature = enable: feat: "--${if enable then "enable" else "disable"}-${feat}";
/* Create a fixed width string with additional prefix to match
required width.
# Create a fixed width string with additional prefix to match Example:
# required width. fixedWidthString 5 "0" (toString 15)
=> "00015"
*/
fixedWidthString = width: filler: str: fixedWidthString = width: filler: str:
let let
strw = lib.stringLength str; strw = lib.stringLength str;
@ -223,25 +403,58 @@ rec {
assert strw <= width; assert strw <= width;
if strw == width then str else filler + fixedWidthString reqWidth filler str; if strw == width then str else filler + fixedWidthString reqWidth filler str;
/* Format a number adding leading zeroes up to fixed width.
# Format a number adding leading zeroes up to fixed width. Example:
fixedWidthNumber 5 15
=> "00015"
*/
fixedWidthNumber = width: n: fixedWidthString width "0" (toString n); fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
/* Check whether a value is a store path.
# Check whether a value is a store path. Example:
isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python"
=> false
isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/"
=> true
isStorePath pkgs.python
=> true
*/
isStorePath = x: builtins.substring 0 1 (toString x) == "/" && dirOf (builtins.toPath x) == builtins.storeDir; isStorePath = x: builtins.substring 0 1 (toString x) == "/" && dirOf (builtins.toPath x) == builtins.storeDir;
# Convert string to int /* Convert string to int
# Obviously, it is a bit hacky to use fromJSON that way. Obviously, it is a bit hacky to use fromJSON that way.
Example:
toInt "1337"
=> 1337
toInt "-4"
=> -4
toInt "3.14"
=> error: floating point JSON numbers are not supported
*/
toInt = str: toInt = str:
let may_be_int = builtins.fromJSON str; in let may_be_int = builtins.fromJSON str; in
if builtins.isInt may_be_int if builtins.isInt may_be_int
then may_be_int then may_be_int
else throw "Could not convert ${str} to int."; else throw "Could not convert ${str} to int.";
# Read a list of paths from `file', relative to the `rootPath'. Lines /* Read a list of paths from `file', relative to the `rootPath'. Lines
# beginning with `#' are treated as comments and ignored. Whitespace beginning with `#' are treated as comments and ignored. Whitespace
# is significant. is significant.
NOTE: this function is not performant and should be avoided
Example:
readPathsFromFile /prefix
./pkgs/development/libraries/qt-5/5.4/qtbase/series
=> [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch"
"/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch"
"/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch"
"/prefix/nix-profiles-library-paths.patch"
"/prefix/compose-search-path.patch" ]
*/
readPathsFromFile = rootPath: file: readPathsFromFile = rootPath: file:
let let
root = toString rootPath; root = toString rootPath;
@ -253,5 +466,4 @@ rec {
absolutePaths = builtins.map (path: builtins.toPath (root + "/" + path)) relativePaths; absolutePaths = builtins.map (path: builtins.toPath (root + "/" + path)) relativePaths;
in in
absolutePaths; absolutePaths;
} }