diff --git a/lib/lists.nix b/lib/lists.nix index 9ecd8f22003..be541427c24 100644 --- a/lib/lists.nix +++ b/lib/lists.nix @@ -1,4 +1,5 @@ # General list operations. + { lib }: with lib.trivial; let @@ -8,21 +9,23 @@ rec { 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]' + /* 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. + Type: singleton :: a -> [a] + Example: singleton "foo" => [ "foo" ] */ singleton = x: [x]; - /* “right fold” a binary function `op' between successive elements of - `list' with `nul' as the starting value, i.e., - `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. - Type: - foldr :: (a -> b -> b) -> b -> [a] -> b + /* “right fold” a binary function `op` between successive elements of + `list` with `nul' as the starting value, i.e., + `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`. + + Type: foldr :: (a -> b -> b) -> b -> [a] -> b Example: concat = foldr (a: b: a + b) "z" @@ -42,16 +45,15 @@ rec { else op (elemAt list n) (fold' (n + 1)); in fold' 0; - /* `fold' is an alias of `foldr' for historic reasons */ + /* `fold` is an alias of `foldr` for historic reasons */ # FIXME(Profpatsch): deprecate? fold = foldr; - /* “left fold”, like `foldr', but from the left: + /* “left fold”, like `foldr`, but from the left: `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`. - Type: - foldl :: (b -> a -> b) -> b -> [a] -> b + Type: foldl :: (b -> a -> b) -> b -> [a] -> b Example: lconcat = foldl (a: b: a + b) "z" @@ -70,16 +72,20 @@ rec { else op (foldl' (n - 1)) (elemAt list n); 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.) + + Type: foldl' :: (b -> a -> b) -> b -> [a] -> b */ foldl' = builtins.foldl' or foldl; /* Map with index starting from 0 + Type: imap0 :: (int -> a -> b) -> [a] -> [b] + Example: imap0 (i: v: "${v}-${toString i}") ["a" "b"] => [ "a-0" "b-1" ] @@ -88,6 +94,8 @@ rec { /* Map with index starting from 1 + Type: imap1 :: (int -> a -> b) -> [a] -> [b] + Example: imap1 (i: v: "${v}-${toString i}") ["a" "b"] => [ "a-1" "b-2" ] @@ -96,6 +104,8 @@ rec { /* Map and concatenate the result. + Type: concatMap :: (a -> [b]) -> [a] -> [b] + Example: concatMap (x: [x] ++ ["z"]) ["a" "b"] => [ "a" "z" "b" "z" ] @@ -118,15 +128,21 @@ rec { /* Remove elements equal to 'e' from a list. Useful for buildInputs. + Type: remove :: a -> [a] -> [a] + Example: remove 3 [ 1 3 4 3 ] => [ 1 4 ] */ - remove = e: filter (x: x != e); + remove = + # Element to remove from the list + 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. + predicate, returns `default` if no such element exists, or + `multiple` if there are multiple matching elements. + + Type: findSingle :: (a -> bool) -> a -> a -> [a] -> a Example: findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ] @@ -136,14 +152,24 @@ rec { findSingle (x: x == 3) "none" "multiple" [ 1 9 ] => "none" */ - findSingle = pred: default: multiple: list: + findSingle = + # Predicate + pred: + # Default value to return if element was not found. + default: + # Default value to return if more than one element was found + multiple: + # Input list + list: let found = filter pred list; len = length found; in if len == 0 then default else if len != 1 then multiple else head found; /* Find the first element in the list matching the specified - predicate or returns `default' if no such element exists. + predicate or return `default` if no such element exists. + + Type: findFirst :: (a -> bool) -> a -> [a] -> a Example: findFirst (x: x > 3) 7 [ 1 6 4 ] @@ -151,12 +177,20 @@ rec { findFirst (x: x > 9) 7 [ 1 6 4 ] => 7 */ - findFirst = pred: default: list: + findFirst = + # Predicate + pred: + # Default value to return + default: + # Input list + list: let found = filter pred list; in if found == [] then default else head found; - /* Return true iff function `pred' returns true for at least element - of `list'. + /* Return true if function `pred` returns true for at least one + element of `list`. + + Type: any :: (a -> bool) -> [a] -> bool Example: any isString [ 1 "a" { } ] @@ -166,8 +200,10 @@ rec { */ any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false); - /* Return true iff function `pred' returns true for all elements of - `list'. + /* Return true if function `pred` returns true for all elements of + `list`. + + Type: all :: (a -> bool) -> [a] -> bool Example: all (x: x < 3) [ 1 2 ] @@ -177,19 +213,25 @@ rec { */ all = builtins.all or (pred: foldr (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 elements of `list` match the supplied predicate + function. + + Type: count :: (a -> bool) -> [a] -> int Example: 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 = + # Predicate + 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'). + Type: optional :: bool -> a -> [a] + Example: optional true "foo" => [ "foo" ] @@ -200,13 +242,19 @@ rec { /* Return a list or an empty list, depending on a boolean value. + Type: optionals :: bool -> [a] -> [a] + Example: optionals true [ 2 3 ] => [ 2 3 ] optionals false [ 2 3 ] => [ ] */ - optionals = cond: elems: if cond then elems else []; + optionals = + # Condition + cond: + # List to return if condition is true + elems: if cond then elems else []; /* If argument is a list, return it; else, wrap it in a singleton @@ -223,20 +271,28 @@ rec { /* Return a list of integers from `first' up to and including `last'. + Type: range :: int -> int -> [int] + Example: range 2 4 => [ 2 3 4 ] range 3 2 => [ ] */ - range = first: last: + range = + # First integer in the range + first: + # Last integer in the range + last: if first > last then [] else genList (n: first + n) (last - first + 1); - /* Splits the elements of a list in two lists, `right' and - `wrong', depending on the evaluation of a predicate. + /* Splits the elements of a list in two lists, `right` and + `wrong`, depending on the evaluation of a predicate. + + Type: (a -> bool) -> [a] -> { right :: [a], wrong :: [a] } Example: partition (x: x > 2) [ 5 1 2 3 4 ] @@ -252,7 +308,7 @@ rec { /* Splits the elements of a list into many lists, using the return value of a predicate. Predicate should return a string which becomes keys of attrset `groupBy' returns. - `groupBy'' allows to customise the combining function and initial value + `groupBy'` allows to customise the combining function and initial value Example: groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ] @@ -268,10 +324,6 @@ rec { xfce = [ { name = "xfce"; script = "xfce4-session &"; } ]; } - - groupBy' allows to customise the combining function and initial value - - Example: groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ] => { true = 12; false = 3; } */ @@ -289,17 +341,27 @@ rec { the merging stops at the shortest. How both lists are merged is defined by the first argument. + Type: zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c] + Example: zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"] => ["he" "lo"] */ - zipListsWith = f: fst: snd: + zipListsWith = + # Function to zip elements of both lists + f: + # First list + fst: + # Second list + snd: genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd)); /* Merges two lists of the same size together. If the sizes aren't the same the merging stops at the shortest. + Type: zipLists :: [a] -> [b] -> [{ fst :: a, snd :: b}] + Example: zipLists [ 1 2 ] [ "a" "b" ] => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ] @@ -308,6 +370,8 @@ rec { /* Reverse the order of the elements of a list. + Type: reverseList :: [a] -> [a] + Example: reverseList [ "b" "o" "j" ] @@ -321,8 +385,7 @@ rec { `before a b == true` means that `b` depends on `a` (there's an edge from `b` to `a`). - Examples: - + Example: listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ] == { minimal = "/"; # minimal element visited = [ "/home/user" ]; # seen elements (in reverse order) @@ -336,7 +399,6 @@ rec { rest = [ "/home" "other" ]; # everything else */ - listDfs = stopOnCycles: before: list: let dfs' = us: visited: rest: @@ -361,7 +423,7 @@ rec { `before a b == true` means that `b` should be after `a` in the result. - Examples: + Example: toposort hasPrefix [ "/home/user" "other" "/" "/home" ] == { result = [ "/" "/home" "/home/user" "other" ]; } @@ -376,7 +438,6 @@ rec { toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; } */ - toposort = before: list: let dfsthis = listDfs true before list; @@ -467,26 +528,38 @@ rec { /* Return the first (at most) N elements of a list. + Type: take :: int -> [a] -> [a] + Example: take 2 [ "a" "b" "c" "d" ] => [ "a" "b" ] take 2 [ ] => [ ] */ - take = count: sublist 0 count; + take = + # Number of elements to take + count: sublist 0 count; /* Remove the first (at most) N elements of a list. + Type: drop :: int -> [a] -> [a] + Example: drop 2 [ "a" "b" "c" "d" ] => [ "c" "d" ] drop 2 [ ] => [ ] */ - drop = count: list: sublist count (length list) list; + drop = + # Number of elements to drop + count: + # Input list + list: sublist count (length list) list; - /* 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`, + starting at index `start`. + + Type: sublist :: int -> int -> [a] -> [a] Example: sublist 1 3 [ "a" "b" "c" "d" "e" ] @@ -494,7 +567,13 @@ rec { sublist 1 3 [ ] => [ ] */ - sublist = start: count: list: + sublist = + # Index at which to start the sublist + start: + # Number of elements to take + count: + # Input list + list: let len = length list; in genList (n: elemAt list (n + start)) @@ -504,6 +583,10 @@ rec { /* Return the last element of a list. + This function throws an error if the list is empty. + + Type: last :: [a] -> a + Example: last [ 1 2 3 ] => 3 @@ -512,7 +595,11 @@ rec { assert lib.assertMsg (list != []) "lists.last: list must not be empty!"; elemAt list (length list - 1); - /* Return all elements but the last + /* Return all elements but the last. + + This function throws an error if the list is empty. + + Type: init :: [a] -> [a] Example: init [ 1 2 3 ] @@ -523,7 +610,7 @@ rec { take (length list - 1) list; - /* return the image of the cross product of some lists by a function + /* Return the image of the cross product of some lists by a function. Example: crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]] @@ -534,8 +621,9 @@ rec { /* Remove duplicate elements from the list. O(n^2) complexity. - Example: + Type: unique :: [a] -> [a] + Example: unique [ 3 2 3 4 ] => [ 3 2 4 ] */