summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorGraham Christensen <graham@grahamc.com>2018-10-29 11:00:02 +0000
committerGitHub <noreply@github.com>2018-10-29 11:00:02 +0000
commit0c5d9e5c5261ef66da47579d1f99747502a57843 (patch)
treeec708117c727bcca198e52b527c5090378a76367 /lib
parent83a434bbab387a2817d9750ec4af9026c00ea3ed (diff)
parent238496688010e3cffd19bbd7d04118ba7d1a52a1 (diff)
downloadnixlib-0c5d9e5c5261ef66da47579d1f99747502a57843.tar
nixlib-0c5d9e5c5261ef66da47579d1f99747502a57843.tar.gz
nixlib-0c5d9e5c5261ef66da47579d1f99747502a57843.tar.bz2
nixlib-0c5d9e5c5261ef66da47579d1f99747502a57843.tar.lz
nixlib-0c5d9e5c5261ef66da47579d1f99747502a57843.tar.xz
nixlib-0c5d9e5c5261ef66da47579d1f99747502a57843.tar.zst
nixlib-0c5d9e5c5261ef66da47579d1f99747502a57843.zip
Merge pull request #49383 from tazjin/docs/lib-docstrings
Update library function "docstrings" for nixdoc generation
Diffstat (limited to 'lib')
-rw-r--r--lib/debug.nix94
-rw-r--r--lib/lists.nix184
-rw-r--r--lib/options.nix155
-rw-r--r--lib/strings.nix204
-rw-r--r--lib/trivial.nix117
5 files changed, 539 insertions, 215 deletions
diff --git a/lib/debug.nix b/lib/debug.nix
index 383eb32d75d0..2879f72ed2ba 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 supplied value after applying a function to it, and
+     return the original value.
 
-  /* Trace the value and also return it.
+     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 = <CODE>; }
@@ -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 9ecd8f220038..be541427c247 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 0e3421175306..791930eafbd0 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 99399459bb48..4d7fa1e774d5 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.
 
-     Example:
+     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 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.
 
-  /* Construct a Unix-style search path, using given package output.
-     If no output 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 <nixpkgs> { }
        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 a string without the specified suffix, if the suffix matches.
 
-  /* Return the prefix of the second argument if the first argument matches
-     its suffix.
+     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 938df6ced476..e31cf73d27c4 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}";