From c2a744b9af3bdd5c66b24eec493607948fc0e227 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 27 Oct 2018 16:40:04 +0200 Subject: [PATCH 1/5] lib/trivial: Update function comments for doc generation Expands on some of the function comments and add some of the special syntaxes recognised by nixdoc. --- lib/trivial.nix | 117 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 30 deletions(-) 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}"; From 0560caa57893a3c8dd11aeece110672c720c9496 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 27 Oct 2018 17:44:17 +0200 Subject: [PATCH 2/5] lib/strings: Update documentation comments for doc generation Updates documentation comments with extra information for nixdoc[1] compatibility. Some documentation strings have additionally been reworded for clarity. "Faux types" are added where applicable, but some functions do things that are not trivially representable in the type notation used so they were ignored for this purpose. [1]: https://github.com/tazjin/nixdoc --- lib/strings.nix | 204 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 153 insertions(+), 51 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index 0c4095bb55c..d6e449552ec 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" @@ -506,8 +604,9 @@ rec { && builtins.substring 0 1 (toString x) == "/" && dirOf x == builtins.storeDir; - /* 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" @@ -517,17 +616,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 @@ -549,6 +649,8 @@ rec { /* Read the contents of a file removing the trailing \n + Type: fileContents :: path -> string + Example: $ echo "1.0" > ./version From 65f50a9bb01a6e2041db1a4e41449e8ea0436adc Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 27 Oct 2018 19:23:06 +0200 Subject: [PATCH 3/5] lib/lists: Update documentation comments for doc generation Updates documentation comments with extra information for nixdoc[1] compatibility. [1]: https://github.com/tazjin/nixdoc --- lib/lists.nix | 184 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 136 insertions(+), 48 deletions(-) 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 ] */ From da18b9263505e957f41c97d801557fd8e5535c79 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 28 Oct 2018 20:51:26 +0100 Subject: [PATCH 4/5] lib/debug: Update documentation comments for docs generation Documents functions in `lib.debug` for docs generation with nixdoc. Note that type signatures and clearer descriptions are still missing on some of these functions, but this is good enough for a first run. --- lib/debug.nix | 94 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 19 deletions(-) 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; }; From 238496688010e3cffd19bbd7d04118ba7d1a52a1 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 28 Oct 2018 21:31:35 +0100 Subject: [PATCH 5/5] lib/options: Update documentation comments for docs generation Documents functions in `lib.options` for docs generation with nixdoc. The formatting change in the `mkOption` arguments is due to the way `nixdoc` parses documentation comments on pattern arguments. It's not ideal, but it works. --- lib/options.nix | 155 +++++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 67 deletions(-) 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