diff --git a/lib/debug.nix b/lib/debug.nix index 383eb32d75d..2879f72ed2b 100644 --- a/lib/debug.nix +++ b/lib/debug.nix @@ -23,27 +23,54 @@ rec { # -- TRACING -- - /* Trace msg, but only if pred is true. + /* Conditionally trace the supplied message, based on a predicate. + + Type: traceIf :: bool -> string -> a -> a Example: traceIf true "hello" 3 trace: hello => 3 */ - traceIf = pred: msg: x: if pred then trace msg x else x; + traceIf = + # Predicate to check + pred: + # Message that should be traced + msg: + # Value to return + x: if pred then trace msg x else x; - /* Trace the value and also return it. + /* Trace the supplied value after applying a function to it, and + return the original value. + + Type: traceValFn :: (a -> b) -> a -> a Example: traceValFn (v: "mystring ${v}") "foo" trace: mystring foo => "foo" */ - traceValFn = f: x: trace (f x) x; + traceValFn = + # Function to apply + f: + # Value to trace and return + x: trace (f x) x; + + /* Trace the supplied value and return it. + + Type: traceVal :: a -> a + + Example: + traceVal 42 + # trace: 42 + => 42 + */ traceVal = traceValFn id; /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. + Type: traceSeq :: a -> b -> b + Example: trace { a.b.c = 3; } null trace: { a = ; } @@ -52,7 +79,11 @@ rec { trace: { a = { b = { c = 3; }; }; } => null */ - traceSeq = x: y: trace (builtins.deepSeq x x) y; + traceSeq = + # The value to trace + x: + # The value to return + y: trace (builtins.deepSeq x x) y; /* Like `traceSeq`, but only evaluate down to depth n. This is very useful because lots of `traceSeq` usages @@ -76,27 +107,49 @@ rec { in trace (generators.toPretty { allowPrettyValues = true; } (modify depth snip x)) y; - /* A combination of `traceVal` and `traceSeq` */ - traceValSeqFn = f: v: traceValFn f (builtins.deepSeq v v); + /* A combination of `traceVal` and `traceSeq` that applies a + provided function to the value to be traced after `deepSeq`ing + it. + */ + traceValSeqFn = + # Function to apply + f: + # Value to trace + v: traceValFn f (builtins.deepSeq v v); + + /* A combination of `traceVal` and `traceSeq`. */ traceValSeq = traceValSeqFn id; + /* A combination of `traceVal` and `traceSeqN` that applies a + provided function to the value to be traced. */ + traceValSeqNFn = + # Function to apply + f: + depth: + # Value to trace + v: traceSeqN depth (f v) v; + /* A combination of `traceVal` and `traceSeqN`. */ - traceValSeqNFn = f: depth: v: traceSeqN depth (f v) v; traceValSeqN = traceValSeqNFn id; # -- TESTING -- - /* Evaluate a set of tests. A test is an attribute set {expr, - expected}, denoting an expression and its expected result. The - result is a list of failed tests, each represented as {name, - expected, actual}, denoting the attribute name of the failing - test and its expected and actual results. Used for regression - testing of the functions in lib; see tests.nix for an example. - Only tests having names starting with "test" are run. - Add attr { tests = ["testName"]; } to run these test only + /* Evaluate a set of tests. A test is an attribute set `{expr, + expected}`, denoting an expression and its expected result. The + result is a list of failed tests, each represented as `{name, + expected, actual}`, denoting the attribute name of the failing + test and its expected and actual results. + + Used for regression testing of the functions in lib; see + tests.nix for an example. Only tests having names starting with + "test" are run. + + Add attr { tests = ["testName"]; } to run these tests only. */ - runTests = tests: lib.concatLists (lib.attrValues (lib.mapAttrs (name: test: + runTests = + # Tests to run + tests: lib.concatLists (lib.attrValues (lib.mapAttrs (name: test: let testsToRun = if tests ? tests then tests.tests else []; in if (substring 0 4 name == "test" || elem name testsToRun) && ((testsToRun == []) || elem name tests.tests) @@ -105,8 +158,11 @@ rec { then [ { inherit name; expected = test.expected; result = test.expr; } ] else [] ) tests)); - # create a test assuming that list elements are true - # usage: { testX = allTrue [ true ]; } + /* Create a test assuming that list elements are `true`. + + Example: + { testX = allTrue [ true ]; } + */ testAllTrue = expr: { inherit expr; expected = map (x: true) expr; }; 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 ] */ diff --git a/lib/options.nix b/lib/options.nix index 0e342117530..791930eafbd 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -8,61 +8,72 @@ with lib.strings; rec { - # Returns true when the given argument is an option - # - # Examples: - # isOption 1 // => false - # isOption (mkOption {}) // => true + /* Returns true when the given argument is an option + + Type: isOption :: a -> bool + + Example: + isOption 1 // => false + isOption (mkOption {}) // => true + */ isOption = lib.isType "option"; - # Creates an Option attribute set. mkOption accepts an attribute set with the following keys: - # - # default: Default value used when no definition is given in the configuration. - # defaultText: Textual representation of the default, for in the manual. - # example: Example value used in the manual. - # description: String describing the option. - # type: Option type, providing type-checking and value merging. - # apply: Function that converts the option value to something else. - # internal: Whether the option is for NixOS developers only. - # visible: Whether the option shows up in the manual. - # readOnly: Whether the option can be set only once - # options: Obsolete, used by types.optionSet. - # - # All keys default to `null` when not given. - # - # Examples: - # mkOption { } // => { _type = "option"; } - # mkOption { defaultText = "foo"; } // => { _type = "option"; defaultText = "foo"; } + /* Creates an Option attribute set. mkOption accepts an attribute set with the following keys: + + All keys default to `null` when not given. + + Example: + mkOption { } // => { _type = "option"; } + mkOption { defaultText = "foo"; } // => { _type = "option"; defaultText = "foo"; } + */ mkOption = - { default ? null # Default value used when no definition is given in the configuration. - , defaultText ? null # Textual representation of the default, for in the manual. - , example ? null # Example value used in the manual. - , description ? null # String describing the option. - , relatedPackages ? null # Related packages used in the manual (see `genRelatedPackages` in ../nixos/doc/manual/default.nix). - , type ? null # Option type, providing type-checking and value merging. - , apply ? null # Function that converts the option value to something else. - , internal ? null # Whether the option is for NixOS developers only. - , visible ? null # Whether the option shows up in the manual. - , readOnly ? null # Whether the option can be set only once - , options ? null # Obsolete, used by types.optionSet. + { + # Default value used when no definition is given in the configuration. + default ? null, + # Textual representation of the default, for the manual. + defaultText ? null, + # Example value used in the manual. + example ? null, + # String describing the option. + description ? null, + # Related packages used in the manual (see `genRelatedPackages` in ../nixos/doc/manual/default.nix). + relatedPackages ? null, + # Option type, providing type-checking and value merging. + type ? null, + # Function that converts the option value to something else. + apply ? null, + # Whether the option is for NixOS developers only. + internal ? null, + # Whether the option shows up in the manual. + visible ? null, + # Whether the option can be set only once + readOnly ? null, + # Obsolete, used by types.optionSet. + options ? null } @ attrs: attrs // { _type = "option"; }; - # Creates a Option attribute set for a boolean value option i.e an option to be toggled on or off: - # - # Examples: - # mkEnableOption "foo" // => { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; } - mkEnableOption = name: mkOption { + /* Creates an Option attribute set for a boolean value option i.e an + option to be toggled on or off: + + Example: + mkEnableOption "foo" + => { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; } + */ + mkEnableOption = + # Name for the created option + name: mkOption { default = false; example = true; description = "Whether to enable ${name}."; type = lib.types.bool; }; - # This option accept anything, but it does not produce any result. This - # is useful for sharing a module across different module sets without - # having to implement similar features as long as the value of the options - # are not expected. + /* This option accepts anything, but it does not produce any result. + + This is useful for sharing a module across different module sets + without having to implement similar features as long as the + values of the options are not accessed. */ mkSinkUndeclaredOptions = attrs: mkOption ({ internal = true; visible = false; @@ -102,18 +113,24 @@ rec { else val) (head defs).value defs; - # Extracts values of all "value" keys of the given list - # - # Examples: - # getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ] - # getValues [ ] // => [ ] + /* Extracts values of all "value" keys of the given list. + + Type: getValues :: [ { value :: a } ] -> [a] + + Example: + getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ] + getValues [ ] // => [ ] + */ getValues = map (x: x.value); - # Extracts values of all "file" keys of the given list - # - # Examples: - # getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ] - # getFiles [ ] // => [ ] + /* Extracts values of all "file" keys of the given list + + Type: getFiles :: [ { file :: a } ] -> [a] + + Example: + getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ] + getFiles [ ] // => [ ] + */ getFiles = map (x: x.file); # Generate documentation template from the list of option declaration like @@ -146,10 +163,13 @@ rec { /* This function recursively removes all derivation attributes from - `x' except for the `name' attribute. This is to make the - generation of `options.xml' much more efficient: the XML - representation of derivations is very large (on the order of - megabytes) and is not actually used by the manual generator. */ + `x` except for the `name` attribute. + + This is to make the generation of `options.xml` much more + efficient: the XML representation of derivations is very large + (on the order of megabytes) and is not actually used by the + manual generator. + */ scrubOptionValue = x: if isDerivation x then { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; } @@ -158,20 +178,21 @@ rec { else x; - /* For use in the ‘example’ option attribute. It causes the given - text to be included verbatim in documentation. This is necessary - for example values that are not simple values, e.g., - functions. */ + /* For use in the `example` option attribute. It causes the given + text to be included verbatim in documentation. This is necessary + for example values that are not simple values, e.g., functions. + */ literalExample = text: { _type = "literalExample"; inherit text; }; + # Helper functions. - /* Helper functions. */ + /* Convert an option, described as a list of the option parts in to a + safe, human readable version. - # Convert an option, described as a list of the option parts in to a - # safe, human readable version. ie: - # - # (showOption ["foo" "bar" "baz"]) == "foo.bar.baz" - # (showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux" + Example: + (showOption ["foo" "bar" "baz"]) == "foo.bar.baz" + (showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux" + */ showOption = parts: let escapeOptionPart = part: let diff --git a/lib/strings.nix b/lib/strings.nix index 99399459bb4..4d7fa1e774d 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -12,6 +12,8 @@ rec { /* Concatenate a list of strings. + Type: concatStrings :: [string] -> string + Example: concatStrings ["foo" "bar"] => "foobar" @@ -20,15 +22,19 @@ rec { /* Map a function over a list and concatenate the resulting strings. + Type: concatMapStrings :: (a -> string) -> [a] -> string + Example: concatMapStrings (x: "a" + x) ["foo" "bar"] => "afooabar" */ concatMapStrings = f: list: concatStrings (map f list); - /* Like `concatMapStrings' except that the f functions also gets the + /* Like `concatMapStrings` except that the f functions also gets the position as a parameter. + Type: concatImapStrings :: (int -> a -> string) -> [a] -> string + Example: concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"] => "1-foo2-bar" @@ -37,17 +43,25 @@ rec { /* Place an element between each element of a list + Type: intersperse :: a -> [a] -> [a] + Example: intersperse "/" ["usr" "local" "bin"] => ["usr" "/" "local" "/" "bin"]. */ - intersperse = separator: list: + intersperse = + # Separator to add between elements + separator: + # Input list + list: if list == [] || length list == 1 then list else tail (lib.concatMap (x: [separator x]) list); /* Concatenate a list of strings with a separator between each element + Type: concatStringsSep :: string -> [string] -> string + Example: concatStringsSep "/" ["usr" "local" "bin"] => "usr/local/bin" @@ -55,43 +69,77 @@ rec { concatStringsSep = builtins.concatStringsSep or (separator: list: concatStrings (intersperse separator list)); - /* First maps over the list and then concatenates it. + /* Maps a function over a list of strings and then concatenates the + result with the specified separator interspersed between + elements. + + Type: concatMapStringsSep :: string -> (string -> string) -> [string] -> string Example: concatMapStringsSep "-" (x: toUpper x) ["foo" "bar" "baz"] => "FOO-BAR-BAZ" */ - concatMapStringsSep = sep: f: list: concatStringsSep sep (map f list); + concatMapStringsSep = + # Separator to add between elements + sep: + # Function to map over the list + f: + # List of input strings + list: concatStringsSep sep (map f list); - /* First imaps over the list and then concatenates it. + /* Same as `concatMapStringsSep`, but the mapping function + additionally receives the position of its argument. + + Type: concatMapStringsSep :: string -> (int -> string -> string) -> [string] -> string Example: - concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ] => "6-3-2" */ - concatImapStringsSep = sep: f: list: concatStringsSep sep (lib.imap1 f list); + concatImapStringsSep = + # Separator to add between elements + sep: + # Function that receives elements and their positions + f: + # List of input strings + list: concatStringsSep sep (lib.imap1 f list); - /* Construct a Unix-style search path consisting of each `subDir" - directory of the given list of packages. + /* Construct a Unix-style, colon-separated search path consisting of + the given `subDir` appended to each of the given paths. + + Type: makeSearchPath :: string -> [string] -> string Example: makeSearchPath "bin" ["/root" "/usr" "/usr/local"] => "/root/bin:/usr/bin:/usr/local/bin" - makeSearchPath "bin" ["/"] - => "//bin" + makeSearchPath "bin" [""] + => "/bin" */ - makeSearchPath = subDir: packages: - concatStringsSep ":" (map (path: path + "/" + subDir) (builtins.filter (x: x != null) packages)); + makeSearchPath = + # Directory name to append + subDir: + # List of base paths + paths: + concatStringsSep ":" (map (path: path + "/" + subDir) (builtins.filter (x: x != null) paths)); - /* Construct a Unix-style search path, using given package output. - If no output is found, fallback to `.out` and then to the default. + /* Construct a Unix-style search path by appending the given + `subDir` to the specified `output` of each of the packages. If no + output by the given name is found, fallback to `.out` and then to + the default. + + Type: string -> string -> [package] -> string Example: makeSearchPathOutput "dev" "bin" [ pkgs.openssl pkgs.zlib ] => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/bin:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/bin" */ - makeSearchPathOutput = output: subDir: pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs); + makeSearchPathOutput = + # Package output to use + output: + # Directory name to append + subDir: + # List of packages + pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs); /* Construct a library search path (such as RPATH) containing the libraries for a set of packages @@ -117,13 +165,12 @@ rec { /* Construct a perl search path (such as $PERL5LIB) - FIXME(zimbatm): this should be moved in perl-specific code - Example: pkgs = import { } makePerlPath [ pkgs.perlPackages.libnet ] => "/nix/store/n0m1fk9c960d8wlrs62sncnadygqqc6y-perl-Net-SMTP-1.25/lib/perl5/site_perl" */ + # FIXME(zimbatm): this should be moved in perl-specific code makePerlPath = makeSearchPathOutput "lib" "lib/perl5/site_perl"; /* Construct a perl search path recursively including all dependencies (such as $PERL5LIB) @@ -138,34 +185,51 @@ rec { /* Depending on the boolean `cond', return either the given string or the empty string. Useful to concatenate against a bigger string. + Type: optionalString :: bool -> string -> string + Example: optionalString true "some-string" => "some-string" optionalString false "some-string" => "" */ - optionalString = cond: string: if cond then string else ""; + optionalString = + # Condition + cond: + # String to return if condition is true + string: if cond then string else ""; /* Determine whether a string has given prefix. + Type: hasPrefix :: string -> string -> bool + Example: hasPrefix "foo" "foobar" => true hasPrefix "foo" "barfoo" => false */ - hasPrefix = pref: str: - substring 0 (stringLength pref) str == pref; + hasPrefix = + # Prefix to check for + pref: + # Input string + str: substring 0 (stringLength pref) str == pref; /* Determine whether a string has given suffix. + Type: hasSuffix :: string -> string -> bool + Example: hasSuffix "foo" "foobar" => false hasSuffix "foo" "barfoo" => true */ - hasSuffix = suffix: content: + hasSuffix = + # Suffix to check for + suffix: + # Input string + content: let lenContent = stringLength content; lenSuffix = stringLength suffix; @@ -180,6 +244,8 @@ rec { Also note that Nix treats strings as a list of bytes and thus doesn't handle unicode. + Type: stringtoCharacters :: string -> [string] + Example: stringToCharacters "" => [ ] @@ -194,18 +260,25 @@ rec { /* Manipulate a string character by character and replace them by strings before concatenating the results. + Type: stringAsChars :: (string -> string) -> string -> string + Example: stringAsChars (x: if x == "a" then "i" else x) "nax" => "nix" */ - stringAsChars = f: s: - concatStrings ( + stringAsChars = + # Function to map over each individual character + f: + # Input string + s: concatStrings ( map f (stringToCharacters s) ); - /* Escape occurrence of the elements of ‘list’ in ‘string’ by + /* Escape occurrence of the elements of `list` in `string` by prefixing it with a backslash. + Type: escape :: [string] -> string -> string + Example: escape ["(" ")"] "(foo)" => "\\(foo\\)" @@ -214,6 +287,8 @@ rec { /* Quote string to be used safely within the Bourne shell. + Type: escapeShellArg :: string -> string + Example: escapeShellArg "esc'ape\nme" => "'esc'\\''ape\nme'" @@ -222,6 +297,8 @@ rec { /* Quote all arguments to be safely passed to the Bourne shell. + Type: escapeShellArgs :: [string] -> string + Example: escapeShellArgs ["one" "two three" "four'five"] => "'one' 'two three' 'four'\\''five'" @@ -230,13 +307,15 @@ rec { /* Turn a string into a Nix expression representing that string + Type: string -> string + Example: escapeNixString "hello\${}\n" => "\"hello\\\${}\\n\"" */ escapeNixString = s: escape ["$"] (builtins.toJSON s); - /* Obsolete - use replaceStrings instead. */ + # Obsolete - use replaceStrings instead. replaceChars = builtins.replaceStrings or ( del: new: s: let @@ -256,6 +335,8 @@ rec { /* Converts an ASCII string to lower-case. + Type: toLower :: string -> string + Example: toLower "HOME" => "home" @@ -264,6 +345,8 @@ rec { /* Converts an ASCII string to upper-case. + Type: toUpper :: string -> string + Example: toUpper "home" => "HOME" @@ -273,7 +356,7 @@ rec { /* Appends string context from another string. This is an implementation detail of Nix. - Strings in Nix carry an invisible `context' which is a list of strings + 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. @@ -319,8 +402,9 @@ rec { in recurse 0 0; - /* Return the suffix of the second argument if the first argument matches - its prefix. + /* Return a string without the specified prefix, if the prefix matches. + + Type: string -> string -> string Example: removePrefix "foo." "foo.bar.baz" @@ -328,18 +412,23 @@ rec { removePrefix "xxx" "foo.bar.baz" => "foo.bar.baz" */ - removePrefix = pre: s: + removePrefix = + # Prefix to remove if it matches + prefix: + # Input string + str: let - preLen = stringLength pre; - sLen = stringLength s; + preLen = stringLength prefix; + sLen = stringLength str; in - if hasPrefix pre s then - substring preLen (sLen - preLen) s + if hasPrefix prefix str then + substring preLen (sLen - preLen) str else - s; + str; - /* Return the prefix of the second argument if the first argument matches - its suffix. + /* Return a string without the specified suffix, if the suffix matches. + + Type: string -> string -> string Example: removeSuffix "front" "homefront" @@ -347,17 +436,21 @@ rec { removeSuffix "xxx" "homefront" => "homefront" */ - removeSuffix = suf: s: + removeSuffix = + # Suffix to remove if it matches + suffix: + # Input string + str: let - sufLen = stringLength suf; - sLen = stringLength s; + sufLen = stringLength suffix; + sLen = stringLength str; in - if sufLen <= sLen && suf == substring (sLen - sufLen) sufLen s then - substring 0 (sLen - sufLen) s + if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then + substring 0 (sLen - sufLen) str else - s; + str; - /* Return true iff string v1 denotes a version older than v2. + /* Return true if string v1 denotes a version older than v2. Example: versionOlder "1.1" "1.2" @@ -367,7 +460,7 @@ rec { */ versionOlder = v1: v2: builtins.compareVersions v2 v1 == 1; - /* Return true iff string v1 denotes a version equal to or newer than v2. + /* Return true if string v1 denotes a version equal to or newer than v2. Example: versionAtLeast "1.1" "1.0" @@ -459,6 +552,11 @@ rec { /* Create a fixed width string with additional prefix to match required width. + This function will fail if the input string is longer than the + requested length. + + Type: fixedWidthString :: int -> string -> string + Example: fixedWidthString 5 "0" (toString 15) => "00015" @@ -509,8 +607,9 @@ rec { else false; - /* Convert string to int - Obviously, it is a bit hacky to use fromJSON that way. + /* Parse a string string as an int. + + Type: string -> int Example: toInt "1337" @@ -520,17 +619,18 @@ rec { toInt "3.14" => error: floating point JSON numbers are not supported */ + # Obviously, it is a bit hacky to use fromJSON this way. toInt = str: let may_be_int = builtins.fromJSON str; in if builtins.isInt may_be_int then may_be_int else throw "Could not convert ${str} to int."; - /* Read a list of paths from `file', relative to the `rootPath'. Lines - beginning with `#' are treated as comments and ignored. Whitespace - is significant. + /* Read a list of paths from `file`, relative to the `rootPath`. + Lines beginning with `#` are treated as comments and ignored. + Whitespace is significant. - NOTE: this function is not performant and should be avoided + NOTE: This function is not performant and should be avoided. Example: readPathsFromFile /prefix @@ -552,6 +652,8 @@ rec { /* Read the contents of a file removing the trailing \n + Type: fileContents :: path -> string + Example: $ echo "1.0" > ./version diff --git a/lib/trivial.nix b/lib/trivial.nix index 938df6ced47..e31cf73d27c 100644 --- a/lib/trivial.nix +++ b/lib/trivial.nix @@ -9,23 +9,37 @@ rec { Type: id :: a -> a */ - id = x: x; + id = + # The value to return + x: x; /* The constant function - Ignores the second argument. - Or: Construct a function that always returns a static value. + + Ignores the second argument. If called with only one argument, + constructs a function that always returns a static value. Type: const :: a -> b -> a Example: let f = const 5; in f 10 => 5 */ - const = x: y: x; + const = + # Value to return + x: + # Value to ignore + y: x; ## Named versions corresponding to some builtin operators. - /* Concatenate two lists */ + /* Concatenate two lists + + Type: concat :: [a] -> [a] -> [a] + + Example: + concat [ 1 2 ] [ 3 4 ] + => [ 1 2 3 4 ] + */ concat = x: y: x ++ y; /* boolean “or” */ @@ -53,27 +67,40 @@ rec { bitNot = builtins.sub (-1); /* Convert a boolean to a string. - Note that toString on a bool returns "1" and "". + + This function uses the strings "true" and "false" to represent + boolean values. Calling `toString` on a bool instead returns "1" + and "" (sic!). + + Type: boolToString :: bool -> string */ boolToString = b: if b then "true" else "false"; /* Merge two attribute sets shallowly, right side trumps left + mergeAttrs :: attrs -> attrs -> attrs + Example: mergeAttrs { a = 1; b = 2; } { b = 3; c = 4; } => { a = 1; b = 3; c = 4; } */ - mergeAttrs = x: y: x // y; + mergeAttrs = + # Left attribute set + x: + # Right attribute set (higher precedence for equal keys) + y: x // y; /* Flip the order of the arguments of a binary function. + Type: flip :: (a -> b -> c) -> (b -> a -> c) + Example: flip concat [1] [2] => [ 2 1 ] */ flip = f: a: b: f b a; - /* Apply function if argument is non-null. + /* Apply function if the supplied argument is non-null. Example: mapNullable (x: x+1) null @@ -81,7 +108,11 @@ rec { mapNullable (x: x+1) 22 => 23 */ - mapNullable = f: a: if isNull a then a else f a; + mapNullable = + # Function to call + f: + # Argument to check for null before passing it to `f` + a: if isNull a then a else f a; # Pull in some builtins not included elsewhere. inherit (builtins) @@ -92,21 +123,27 @@ rec { ## nixpks version strings - # The current full nixpkgs version number. + /* Returns the current full nixpkgs version number. */ version = release + versionSuffix; - # The current nixpkgs version number as string. + /* Returns the current nixpkgs release number as string. */ release = lib.strings.fileContents ../.version; - # The current nixpkgs version suffix as string. + /* Returns the current nixpkgs version suffix as string. */ versionSuffix = let suffixFile = ../.version-suffix; in if pathExists suffixFile then lib.strings.fileContents suffixFile else "pre-git"; - # Attempt to get the revision nixpkgs is from - revisionWithDefault = default: + /* Attempts to return the the current revision of nixpkgs and + returns the supplied default value otherwise. + + Type: revisionWithDefault :: string -> string + */ + revisionWithDefault = + # Default value to return if revision can not be determined + default: let revisionFile = "${toString ./..}/.git-revision"; gitRepo = "${toString ./..}/.git"; @@ -117,14 +154,20 @@ rec { nixpkgsVersion = builtins.trace "`lib.nixpkgsVersion` is deprecated, use `lib.version` instead!" version; - # Whether we're being called by nix-shell. + /* Determine whether the function is being called from inside a Nix + shell. + + Type: inNixShell :: bool + */ inNixShell = builtins.getEnv "IN_NIX_SHELL" != ""; ## Integer operations - # Return minimum/maximum of two numbers. + /* Return minimum of two numbers. */ min = x: y: if x < y then x else y; + + /* Return maximum of two numbers. */ max = x: y: if x > y then x else y; /* Integer modulus @@ -158,8 +201,9 @@ rec { second subtype, compare elements of a single subtype with `yes` and `no` respectively. - Example: + Type: (a -> bool) -> (a -> a -> int) -> (a -> a -> int) -> (a -> a -> int) + Example: let cmp = splitByAndCompare (hasPrefix "foo") compare compare; in cmp "a" "z" => -1 @@ -170,31 +214,44 @@ rec { # while compare "fooa" "a" => 1 */ - splitByAndCompare = p: yes: no: a: b: + splitByAndCompare = + # Predicate + p: + # Comparison function if predicate holds for both values + yes: + # Comparison function if predicate holds for neither value + no: + # First value to compare + a: + # Second value to compare + b: if p a then if p b then yes a b else -1 else if p b then 1 else no a b; - /* Reads a JSON file. */ + /* Reads a JSON file. + + Type :: path -> any + */ importJSON = path: builtins.fromJSON (builtins.readFile path); ## Warnings - /* See https://github.com/NixOS/nix/issues/749. Eventually we'd like these - to expand to Nix builtins that carry metadata so that Nix can filter out - the INFO messages without parsing the message string. + # See https://github.com/NixOS/nix/issues/749. Eventually we'd like these + # to expand to Nix builtins that carry metadata so that Nix can filter out + # the INFO messages without parsing the message string. + # + # Usage: + # { + # foo = lib.warn "foo is deprecated" oldFoo; + # } + # + # TODO: figure out a clever way to integrate location information from + # something like __unsafeGetAttrPos. - Usage: - { - foo = lib.warn "foo is deprecated" oldFoo; - } - - TODO: figure out a clever way to integrate location information from - something like __unsafeGetAttrPos. - */ warn = msg: builtins.trace "WARNING: ${msg}"; info = msg: builtins.trace "INFO: ${msg}";