Repository: tweag/nickel
Pull request #1196 (2023-03-23 13:34:56)

Audit stdlib documentation
For now, a quick pass over the stdlib documentation for consistency and typos.

vkleen: > So I had to stop at some point with suggestions, there were just too many lines

No worries :laughing: I'll find all the lines.

>     * verbal forms. If we start some documentation with `Returns`, then all verbs describing the action of a function should use the `s` form.
👍

>     * indentation. I believe there are cases where the code is indented with respect to the block fences, but not everywhere. Also, I don't think it's standard practice, it feels like it should start the the same level as the block, as I don't see much usefulness in having the whole code block indented by one level from the leftmost boundary.
It always felt to me like a fenced in block acts like a new scope, and as such deserves to be indentend. But that may indeed just be me.

I'll make another pass over all of it, remove indents from code block and pay attention to the verbal forms.
yannham: Sorry if my suggestions sounded a bit too affirmative :sweat_smile: I'm not necessarily pushing for the trailing `s`, and maybe there's a case to be made for indenting code inside a fenced block? Let met quickly check if I can find style advises online. But the hard constraint is that we're consistent.
yannham: So with respect to indenting code, it seems like [GitHub's documentation](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks) and [W3C's documentation](https://www.w3schools.io/file/markdown-code-fence-blocks/) show examples where the code isn't indented indeed (starts at the same indentation as the fence).
yannham: Oh my, I've merged #1198, which might make this rebase quite difficult. Might be simpler in the end to revert, merge this one, and reformat afterward? Or do you think it'll be manageable?

Patch:
diff --git a/flake.lock b/flake.lock
index c90f6b7d..a093492b 100644
--- a/flake.lock
+++ b/flake.lock
@@ -411,11 +411,11 @@
         "rust-overlay": "rust-overlay_4"
       },
       "locked": {
-        "lastModified": 1680007860,
-        "narHash": "sha256-H36jb8pjR3vUl2xKVnazM6Hn+S5dsUmtN8xk9FwhzJo=",
+        "lastModified": 1680178226,
+        "narHash": "sha256-EZtmLYPQII8Ma9yH0udqlNjSXiYUg134j+0Srzb4rbM=",
         "owner": "tweag",
         "repo": "topiary",
-        "rev": "399d59415eef6193e4959b8774b285a2615ab692",
+        "rev": "1af03d77abf6aef3ea36f878554c16619207252b",
         "type": "github"
       },
       "original": {
diff --git a/stdlib/array.ncl b/stdlib/array.ncl
index a1697a64..18d962a7 100644
--- a/stdlib/array.ncl
+++ b/stdlib/array.ncl
@@ -2,15 +2,16 @@
   array = {
     NonEmpty
       | doc m%"
-        Contract to ensure the given array is not empty.
+          Enforces that an array is not empty.
 
-        For example:
-        ```nickel
+          # Examples
+
+          ```nickel
           ([] | NonEmpty) =>
             error
           ([ 1 ] | NonEmpty) =>
             [ 1 ]
-        ```
+          ```
         "%
       = fun label value =>
         if %typeof% value == `Array then
@@ -24,174 +25,183 @@
     first
       : forall a. Array a -> a
       | doc m%"
-        Results in the first element of the given array.
+          Returns the first element of the given array.
 
-        # Examples
+          # Examples
 
-        ```nickel
+          ```nickel
           first [ "this is the head", "this is not" ] =>
             "this is the head"
-        ```
+          ```
         "%
       = fun array => %elem_at% array 0,
 
     last
       : forall a. Array a -> a
       | doc m%"
-        Results in the last element of the given array.
+          Returns the last element of the given array.
 
-        # Examples
+          # Examples
 
-        ```nickel
+          ```nickel
           last [ "this is the head", "this is not" ] =>
             "this is not"
-        ```
+          ```
         "%
       = fun array => %elem_at% array (%length% array - 1),
 
     drop_first
       : forall a. Array a -> Array a
       | doc m%"
-        Results in the given array without the first element.
+          Returns the given array without its first element.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           drop_first [ 1, 2, 3 ] =>
             [ 2, 3 ]
-        ```
+          ```
         "%
       = fun array => %array_slice% 1 (%length% array) array,
 
     drop_last
       : forall a. Array a -> Array a
       | doc m%"
-        Results in the given array without the last element.
+          Returns the given array without its last element.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           drop_last [ 1, 2, 3 ] =>
             [ 1, 2 ]
-        ```
+          ```
         "%
       = fun array => %array_slice% 0 (%length% array - 1) array,
 
     length
       : forall a. Array a -> Number
       | doc m%"
-        Results in a number representing the length of the given array.
+          Returns the length of the given array.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           length [ "Hello,", " World!" ] =>
             2
-        ```
+          ```
         "%
       = fun l => %length% l,
 
     map
       : forall a b. (a -> b) -> Array a -> Array b
       | doc m%"
-        `map f [x1, x2, ..., xn]` applies function `f` to every element in the array,
-        resulting in `[f x1, f x2, ... f xn]`
+          Applies a function to every element in the given array. That is,
+          ```nickel
+          map f [ x1, x2, ..., xn ] =>
+            [ f x1, f x2, ..., f xn ]
+          ```
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           map (fun x => x + 1) [ 1, 2, 3 ] =>
             [ 2, 3, 4 ]
-        ```
+          ```
         "%
       = fun f l => %map% l f,
 
     at
       : forall a. Number -> Array a -> a
       | doc m%"
-        Retrieves the n'th element from a array (0-indexed).
+          Retrieves the n-th element from an array, with indices starting at 0.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           at 3 [ "zero" "one" "two" "three" "four" ] =>
             "three"
-        ```
+          ```
         "%
       = fun n l => %elem_at% l n,
 
     concat
       : forall a. Array a -> Array a -> Array a
       | doc m%"
-        Concatenates two arrays such that the second array is appended to the first.
+          Appends the second array to the first one.
+
+          # Examples
 
-        For example:
-        ```nickel
-          concat [ 1, 2, 3 ] [ 4, 5, 6 ] =>
-            [ 1, 2, 3, 4, 5, 6 ]
-        ```
+          ```nickel
+            concat [ 1, 2, 3 ] [ 4, 5, 6 ] =>
+              [ 1, 2, 3, 4, 5, 6 ]
+          ```
         "%
       = fun l1 l2 => l1 @ l2,
 
     fold_left
       : forall a b. (a -> b -> a) -> a -> Array b -> a
       | doc m%"
-        Fold a function over an array. Folds serve a similar purpose to loops or
-        iterators in a functional language like Nickel. `fold_left` iterates over
-        an array, by repeatedly applying a function over each element, threading
-        an additional arbitrary state (the accumulator, of type `a` in the
-        signature).
-
-        `fold_left f init [x1, x2, ..., xn]` results in `f (... (f (f init x1) x2) ...) xn`.
-
-        This function is strict in the intermediate accumulator.
+          Folds a function over an array. Folds serve a similar purpose to loops or
+          iterators in a functional language like Nickel. `fold_left` iterates over
+          an array, by repeatedly applying a function over each element, threading
+          an additional arbitrary state (the accumulator, of type `a` in the
+          signature).
+
+          `fold_left f init [x1, x2, ..., xn]` results in `f (... (f (f init x1) x2) ...) xn`.
+
+          This function is strict in the intermediate accumulator.
+
+          # Left vs right
+
+          Folds come in two variants, left and right. How to decide which one to
+          use?
+
+          - If the folded function isn't associative (such as subtraction), then
+            each variant will give a different result. The choice is dictacted by
+            which one you need. For example:
+
+            ```nickel
+            array.fold_right (-) 0 [1, 2, 3, 4]
+             => -2
+            array.fold_left (-) 0 [1, 2, 3, 4]
+             => -10
+            ```
+          - If the folded function is associative, both `fold_right` and
+            `fold_left` return the same result. In that case, **`fold_left` is
+            generally preferred**, because it forces the evaluation of the
+            intermediate results resulting in less memory consumption and overall
+            better performance (outside of pathological cases).
+            `fold_left` also iterates from the start of the array, which
+            correponds to the usual behavior of loops and iterators in most
+            programming languages. There is one case where `fold_right` might be
+            preferred, see the next point.
+          - If the folded function is associative but _(left) short-circuiting_,
+            meaning that it can sometimes determine the result without using the
+            right argument, then `fold_right` provides early return. An example is
+            the boolean AND operator `&&`: when evaluating `left && right`, if
+            `left` is `false`, the whole expression will evaluate to `false`
+            without even evaluating `right`. Consider the following expression:
+
+            ```nickel
+            array.replicate 1000 true
+            # gives [false, .. true 1000 times]
+            |> array.prepend false
+            |> array.fold_right (&&) [false]
+            ```
+
+            Here, `fold_right` will stop at the first element, and the operation
+            runs in constant time, given the definition of `fold_right` and the
+            lazy evaluation of Nickel. If we had used `fold_left` instead, which
+            is closer to a standard iterator, we would have iterated over all of
+            the 1000 elements of the array.
 
-        # Left vs right
-
-        Folds come in two variants, left and right. How to decide which one to
-        use?
-
-        - If the folded function isn't associative (such as subtraction), then
-          each variant will give a different result. The choice is dictacted by
-          which one you need. For example:
-
-          ```nickel
-          array.fold_right (-) 0 [1, 2, 3, 4]
-           => -2
-          array.fold_left (-) 0 [1, 2, 3, 4]
-           => -10
-          ```
-        - If the folded function is associative, both `fold_right` and
-          `fold_left` return the same result. In that case, **`fold_left` is
-          generally preferred**, because it forces the evaluation of the
-          intermediate results resulting in less memory consumption and overall
-          better performance (outside of pathological cases).
-          `fold_left` also iterates from the start of the array, which
-          correponds to the usual behavior of loops and iterators in most
-          programming languages. There is one case where `fold_right` might be
-          preferred, see the next point.
-        - If the folded function is associative but _(left) short-circuiting_,
-          meaning that it can sometimes determine the result without using the
-          right argument, then `fold_right` provides early return. An example is
-          the boolean AND operator `&&`: when evaluating `left && right`, if
-          `left` is `false`, the whole expression will evaluate to `false`
-          without even evaluating `right`. Consider the following expression:
+          # Examples
 
           ```nickel
-          array.replicate 1000 true
-          # gives [false, .. true 1000 times]
-          |> array.prepend false
-          |> array.fold_right (&&) [false]
-          ```
-
-          Here, `fold_right` will stop at the first element, and the operation
-          runs in constant time, given the definition of `fold_right` and the
-          lazy evaluation of Nickel. If we had used `fold_left` instead, which
-          is closer to a standard iterator, we would have iterated over all of
-          the 1000 elements of the array.
-
-        # Examples
-
-        ```nickel
           fold_left (fun acc e => acc + e) 0 [ 1, 2, 3 ] =>
             (((0 + 1) + 2) 3) =>
             6
-        ```
+          ```
         "%
       = fun f acc l =>
         let length = %length% l in
@@ -211,26 +221,26 @@
     fold_right
       : forall a b. (a -> b -> b) -> b -> Array a -> b
       | doc m%"
-        Fold a function over an array. Folds serve a similar purpose to loops or
-        iterators in a functional language like Nickel. `fold_right` iterates
-        over an array, by repeatedly applying a function to each element and
-        threading an additional arbitrary state (the accumulator of type `a` in
-        the signature).
+          Folds a function over an array. Folds serve a similar purpose to loops or
+          iterators in a functional language like Nickel. `fold_right` iterates
+          over an array, by repeatedly applying a function to each element and
+          threading an additional arbitrary state (the accumulator of type `a` in
+          the signature).
 
-        `fold_right f init [x1, x2, ..., xn]` results in `f x1 (f x2 (... (f xn init) ...))`.
+          `fold_right f init [x1, x2, ..., xn]` results in `f x1 (f x2 (... (f xn init) ...))`.
 
-        # Left vs right
+          # Left vs right
 
-        Folds come in two variants, left and right. How to decide which one to
-        use? Please refer to the documentation of `fold_left`.
+          Folds come in two variants, left and right. How to decide which one to
+          use? Please refer to the documentation of `fold_left`.
 
-        # Examples
+          # Examples
 
-        ```nickel
+          ```nickel
           fold_right (fun e acc => acc @ [e]) [] [ 1, 2, 3 ] =>
             ((([] @ [3]) @ [2]) @ [1]) =>
             [ 3, 2, 1 ]
-        ```
+          ```
         "%
       = fun f fst l =>
         let length = %length% l in
@@ -246,122 +256,132 @@
     prepend
       : forall a. a -> Array a -> Array a
       | doc m%"
-        Construct an array given the first element and the rest of the array.
+          Builds an array given the first element and the rest of the array.
 
-        For example:
-        ```nickel
+          # Examples
+
+          ```nickel
           prepend 1 [ 2, 3 ] =>
             [ 1, 2, 3 ]
-        ```
+          ```
         "%
       = fun x l => [x] @ l,
 
     append
       : forall a. a -> Array a -> Array a
       | doc m%"
-        Construct an array given the last element and the rest of the array.
+          Builds an array given the last element and the rest of the array.
 
-        For example:
-        ```nickel
+          # Examples
+
+          ```nickel
           append 3 [ 1, 2 ] =>
             [ 1, 2, 3 ]
-        ```
+          ```
         "%
       = fun x l => l @ [x],
 
     reverse
       : forall a. Array a -> Array a
       | doc m%"
-        Reverses the order of a array.
+          Reverses an array.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           reverse [ 1, 2, 3 ] =>
             [ 3, 2, 1 ]
-        ```
+          ```
         "%
       = fun l => fold_left (fun acc e => [e] @ acc) [] l,
 
     filter
       : forall a. (a -> Bool) -> Array a -> Array a
       | doc m%"
-        `filter f xs` keeps all elements from `xs` given that satisfy `f`.
+          `filter f xs` returns an array containing all elements from `xs` that satisfy `f`.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           filter (fun x => x <= 3) [ 4, 3, 2, 5, 1 ] =>
             [ 3, 2, 1 ]
-        ```
+          ```
         "%
       = fun pred l => fold_left (fun acc x => if pred x then acc @ [x] else acc) [] l,
 
     flatten
       : forall a. Array (Array a) -> Array a
       | doc m%"
-        Flatten a array of arrays to a single array, essentially concatenating all arrays in the original array.
+          Concatenates all elements of a given array of arrays.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           flatten [[1, 2], [3, 4]] =>
             [1, 2, 3, 4]
-        ```
+          ```
         "%
       = fun l => fold_right (fun l acc => l @ acc) [] l,
 
     all
       : forall a. (a -> Bool) -> Array a -> Bool
       | doc m%"
-        Results in true if all elements in the given array satisfy the predicate, false otherwise.
+          Returns true if all elements in the given array satisfy the predicate, false otherwise.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           all (fun x => x < 3) [ 1, 2 ] =>
             true
           all (fun x => x < 3) [ 1, 2 3 ] =>
             false
-        ```
+          ```
         "%
       = fun pred l => fold_right (fun x acc => if pred x then acc else false) true l,
 
     any
       : forall a. (a -> Bool) -> Array a -> Bool
       | doc m%"
-        Results in false if no elements in the given array satisfy the predicate, true otherwise.
+          Returns true if at least one element in the given array satisfies the predicate, false otherwise.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           any (fun x => x < 3) [ 1, 2, 3, 4 ] =>
             true
           any (fun x => x < 3) [ 5, 6, 7, 8 ] =>
             false
-        ```
+          ```
         "%
       = fun pred l => fold_right (fun x acc => if pred x then true else acc) false l,
 
     elem
       : forall a. a -> Array a -> Bool
       | doc m%"
-        Results in true if the given element is a member of the array, false otherwise.
+          Returns true if the given value appears in the array, false otherwise.
+
+          # Examples
 
-        For example:
-        ```nickel
+          ```nickel
           elem 3 [ 1, 2, 3, 4, 5 ] =>
             true
-        ```
+          ```
         "%
       = fun elt => any (fun x => x == elt),
 
     partition
       : forall a. (a -> Bool) -> Array a -> { right : Array a, wrong : Array a }
       | doc m%"
-        Partitions the given array in two new arrays: those containing the elements that satisfy the predicate, and those
-        that do not.
+          Partitions an array into two new arrays. `right` will contain all
+          elements that satisfy the predicate, while `wrong` will contain those
+          that do not.
 
-        For example:
-        ```nickel
+          # Examples
+
+          ```nickel
           partition (fun x => x < 5) [ 2, 4, 5, 3, 7, 8, 6 ] =>
             { right = [ 3, 4, 2 ], wrong = [ 6, 8, 7, 5 ] }
-        ```
+          ```
         "%
       = fun pred l =>
         let aux =
@@ -376,27 +396,31 @@
     generate
       : forall a. (Number -> a) -> Number -> Array a
       | doc m%"
-        `generate f n` produces a array of length `n` by applying `f` on increasing numbers:
-         `[ f 0, f 1, ..., f (n - 1) ]`.
-
-        For example:
-        ```nickel
-          generate function.id 4 =>
-            [ 0, 1, 2, 3 ]
-        ```
+          `generate f n` returns an array of length `n` by applying `f` to the integers from 0 to n-1:
+          ```nickel
+          generate f n = [ f 0, f 1, ..., f (n - 1) ]
+          ```
+
+          # Examples
+
+          ```nickel
+          generate (fun x => x * x) 4 =>
+            [ 0, 1, 4, 9 ]
+          ```
         "%
       = fun f n => %generate% n f,
 
     sort
-      | forall a. (a -> a -> [| `Lesser, `Equal, `Greater |]) -> Array a -> Array a
+      : forall a. (a -> a -> [| `Lesser, `Equal, `Greater |]) -> Array a -> Array a
       | doc m%"
-        Sorts the given arrays based on the provided comparison operator.
+          Sorts the given array based on the provided comparison operator.
 
-        For example:
-        ```nickel
+          # Examples
+
+          ```nickel
           sort (fun x y => if x < y then `Lesser else if (x == y) then `Equal else `Greater) [ 4, 5, 1, 2 ] =>
             [ 1, 2, 4, 5 ]
-        ```
+          ```
         "%
       #TODO: maybe inline partition to avoid contract checks?
       = fun cmp array =>
@@ -412,32 +436,34 @@
     flat_map
       : forall a b. (a -> Array b) -> Array a -> Array b
       | doc m%"
-        First maps the given function over the array and then flattens the
-        result.
-
-        For example:
-        ```nickel
-        flat_map (fun x => [x, x]) [1, 2, 3]
-          => [1, 1, 2, 2, 3, 3]
-        ```
-      "%
+          First `map` the given function over the array and then `flatten` the
+          result.
+
+          # Examples
+
+          ```nickel
+          flat_map (fun x => [x, x]) [1, 2, 3]
+            => [1, 1, 2, 2, 3, 3]
+          ```
+        "%
       = fun f xs => map f xs |> flatten,
 
     intersperse
       : forall a. a -> Array a -> Array a
       | doc m%"
-        Intersperse the passed value between the elements of an array.
-
-        For example:
-        ```nickel
-        intersperse ", " [ "Hello", "wonderful", "world!" ]
-          => [ "Hello", ", ", "wonderful", ", ", "world!" ]
-        intersperse ", " [ "Hello" ]
-          => [ "Hello" ]
-        intersperse ", " []
-          => []
-        ```
-      "%
+          Intersperses a value between the elements of an array.
+
+          # Examples
+
+          ```nickel
+          intersperse ", " [ "Hello", "wonderful", "world!" ]
+            => [ "Hello", ", ", "wonderful", ", ", "world!" ]
+          intersperse ", " [ "Hello" ]
+            => [ "Hello" ]
+          intersperse ", " []
+            => []
+          ```
+        "%
       = fun v array =>
         let length = %length% array in
         if length <= 1 then
@@ -570,31 +596,31 @@
     reduce_left
       : forall a. (a -> a -> a) -> Array a -> a
       | doc m%"
-        Reduces the elements to a single one, by repeatedly applying a reducing
-        operation. If the array is empty, returns an empty array.
+          Reduces the elements to a single one, by repeatedly applying a reducing
+          operation. If the array is empty, returns an empty array.
 
-        `reduce_left` associates to the left, that is
-        `reduce_left op [x1, x2, ..., xn]` results in `op (... (op (op x1 x2) x3) ...) xn`.
+          `reduce_left` associates to the left, that is
+          `reduce_left op [x1, x2, ..., xn]` results in `op (... (op (op x1 x2) x3) ...) xn`.
 
-        `reduce_left` is the same as `fold_left`, but uses the first element as
-        the initial accumulator.
+          `reduce_left` is the same as `fold_left`, but uses the first element as
+          the initial accumulator.
 
-        # Left vs right
+          # Left vs right
 
-        The rationale to decide between `fold_left` and `fold_right` applies to
-        `reduce_left` and `reduce_right` as well. See the documentation of
-        `fold_left`.
+          The rationale to decide between `fold_left` and `fold_right` applies to
+          `reduce_left` and `reduce_right` as well. See the documentation of
+          `fold_left`.
 
-        # Examples
+          # Examples
 
-        ```nickel
+          ```nickel
           reduce_left (@) [ [1, 2], [3], [4,5] ]
             => (([1, 2] @ [3]) @ [4,5])
             => [ 1, 2, 4, 5 ]
           reduce_left (-) [ 1, 2, 3, 4]
             => ((1 - 2) - 3) - 4
             => -8
-        ```
+          ```
         "%
       = fun f array =>
         let first = %elem_at% array 0 in
@@ -604,32 +630,32 @@
     reduce_right
       : forall a. (a -> a -> a) -> Array a -> a
       | doc m%"
-        Reduces the elements to a single one, by repeatedly applying a reducing
-        operation. If the array is empty, returns an empty array.
+          Reduces the elements to a single one, by repeatedly applying a reducing
+          operation. If the array is empty, returns an empty array.
 
-        `reduce_right` associates to the right, that is
-        `reduce_right op [x1, x2, ..., xn]` results in
-        `op x1 (op x2 (... (op xn-1 xn) ...))`.
+          `reduce_right` associates to the right, that is
+          `reduce_right op [x1, x2, ..., xn]` results in
+          `op x1 (op x2 (... (op xn-1 xn) ...))`.
 
-        `reduce_right` is the same as `fold_right`, but uses the last element as
-        the initial element.
+          `reduce_right` is the same as `fold_right`, but uses the last element as
+          the initial element.
 
-        # Left vs right
+          # Left vs right
 
-        The rationale to decide between `fold_left` and `fold_right` applies to
-        `reduce_left` and `reduce_right` as well. See the documentation of
-        `fold_left`.
+          The rationale to decide between `fold_left` and `fold_right` applies to
+          `reduce_left` and `reduce_right` as well. See the documentation of
+          `fold_left`.
 
-        # Examples
+          # Examples
 
-        ```nickel
+          ```nickel
           reduce_right (@) [ [1, 2], [3], [4,5] ]
             => [1, 2] @ ([3] @ [4,5])
             => [ 1, 2, 4, 5 ]
           reduce_right (-) [ 1, 2, 3, 4]
             => 1 - (2 - (3 - 4))
             => -2
-        ```
+          ```
         "%
       = fun f array =>
         let last_index = %length% array - 1 in
diff --git a/stdlib/builtin.ncl b/stdlib/builtin.ncl
index ab56a03b..f1c9f2f6 100644
--- a/stdlib/builtin.ncl
+++ b/stdlib/builtin.ncl
@@ -3,90 +3,96 @@
     is_number
       : Dyn -> Bool
       | doc m%"
-      Checks if the given value is a number.
+        Checks if a value is a number.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         is_number 1 =>
           true
         is_number "Hello, World!" =>
           false
-      ```
+        ```
       "%
       = fun x => %typeof% x == `Number,
 
     is_bool
       : Dyn -> Bool
       | doc m%"
-      Checks if the given value is a boolean.
+        Checks if a value is a bool.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         is_bool false =>
           true
         is_bool 42 =>
           false
-      ```
+        ```
       "%
       = fun x => %typeof% x == `Bool,
 
     is_string
       : Dyn -> Bool
       | doc m%"
-      Checks if the given value is a string.
+        Checks if a value is a string.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         is_string true =>
           false
         is_string "Hello, World!" =>
           true
-      ```
+        ```
       "%
       = fun x => %typeof% x == `String,
 
     is_enum
       : Dyn -> Bool
       | doc m%"
-      Checks if the given value is an enum tag.
+        Checks if a value is an enum tag.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         is_enum true =>
           false
         is_enum `false =>
           true
-      ```
+        ```
       "%
       = fun x => %typeof% x == `Enum,
 
     is_function
       : Dyn -> Bool
       | doc m%"
-      Checks if the given value is a function.
+        Checks if a value is a function.
+
+        # Examples
 
-      For example
-      ```nickel
+        ```nickel
         is_function (fun x => x) =>
           true
         is_function 42 =>
           false
-      ```
+        ```
       "%
       = fun x => %typeof% x == `Function,
 
     is_array
       : Dyn -> Bool
       | doc m%"
-      Checks if the given value is a array.
+        Checks if a value is an array.
+
+        # Examples
 
-      For example
-      ```nickel
+        ```nickel
         is_array [ 1, 2 ] =>
           true
         is_array 42 =>
           false
-      ```
+        ```
       "%
       = fun x => %typeof% x == `Array,
 
@@ -94,14 +100,16 @@
       : Dyn -> Bool
       | doc m%"
       Checks if the given value is a record.
+        Checks if a value is a record.
 
-      For example
-      ```nickel
+        # Examples
+
+        ```nickel
         is_record [ 1, 2 ] =>
           false
         is_record { hello = "Hello", world = "World" } =>
           true
-      ```
+        ```
       "%
       = fun x => %typeof% x == `Record,
 
@@ -118,159 +126,160 @@
         `Other
       |]
       | doc m%"
-      Results in a value representing the type of the typed value.
+        Returns the type of a value.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         typeof [ 1, 2 ] =>
           `Array
         typeof (fun x => x) =>
           `Function
-      ```
+        ```
       "%
       = fun x => %typeof% x,
 
     seq
       : forall a. Dyn -> a -> a
       | doc m%"
-      `seq x y` forces the evaluation of `x`, before resulting in `y`.
+        `seq x y` forces the evaluation of `x`, before returning `y`.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         seq (42 / 0) 37 =>
           error
         seq (42 / 2) 37 =>
           37
         seq { tooFar = 42 / 0 } 37 =>
           37
-      ```
+        ```
       "%
       = fun x y => %seq% x y,
 
     deep_seq
       : forall a. Dyn -> a -> a
       | doc m%"
-      `deep_seq x y` forces a deep evaluation `x`, before resulting in `y`.
+        `deep_seq x y` forces a deep evaluation `x`, before returning `y`.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         deep_seq (42 / 0) 37 =>
           error
         deep_seq (42 / 2) 37 =>
           37
         deep_seq { tooFar = 42 / 0 } 37 =>
           error
-      ```
+        ```
       "%
       = fun x y => %deep_seq% x y,
 
     hash
       : [| `Md5, `Sha1, `Sha256, `Sha512 |] -> String -> String
       | doc m%"
-      Hashes the given string provided the desired hash algorithm.
+        Hashes the given string with the desired hashing algorithm.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         hash `Md5 "hunter2" =>
           "2ab96390c7dbe3439de74d0c9b0b1767"
-      ```
+        ```
       "%
       = fun type s => %hash% type s,
 
     serialize
       : [| `Json, `Toml, `Yaml |] -> Dyn -> String
       | doc m%"
-      Serializes the given value to the desired representation.
+        Serializes a value into the desired representation.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         serialize `Json { hello = "Hello", world = "World" } =>
           "{
-            "hello": "Hello",
-            "world": "World"
+            \"hello\": \"Hello\",
+            \"world\": \"World\"
           }"
-      ```
+        ```
       "%
       = fun format x => %serialize% format (%force% x),
 
     deserialize
       : [| `Json, `Toml, `Yaml |] -> String -> Dyn
       | doc m%"
-      Deserializes the given string to a nickel value given the encoding of the string.
+        Deserializes a string into a Nickel value from the given representation.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         deserialize `Json "{ \"hello\": \"Hello\", \"world\": \"World\" }"
           { hello = "Hello", world = "World" }
-      ```
+        ```
       "%
       = fun format x => %deserialize% format x,
 
     to_string
       | string.Stringable -> String
       | doc m%"
-      Converts a stringable value to a string representation. Same as
-      `string.from`.
-
-      For example:
-      ```nickel
-      from 42 =>
-        "42"
-      from `Foo =>
-        "Foo"
-      from null =>
-        "null"
-      ```
+        Converts a stringable value to a string representation. Same as
+        `string.from`.
+
+        # Examples
+
+        ```nickel
+        from 42 =>
+          "42"
+        from `Foo =>
+          "Foo"
+        from null =>
+          "null"
+        ```
       "%
       = fun x => %to_str% x,
 
     trace
       : forall a. String -> a -> a
       | doc m%"
-      `builtin.trace msg x` prints `msg` to standard output when encountered by the evaluator,
-      and proceed with the evaluation of `x`.
-
-      For example:
-      ```nickel
-      builtin.trace "Hello, world!" true =>
-        builtin.trace: Hello, world!
-        true
-      ```
+        `builtin.trace msg x` prints `msg` to standard error, then proceeds with
+        the evaluation of `x`.
+
+        # Examples
+
+        ```nickel
+        builtin.trace "Hello, world!" true =>
+          builtin.trace: Hello, world!
+          true
+        ```
       "%
       = fun msg x => %trace% msg x,
 
     FailWith
       | doc m%"
-      A contract that always fails with a given message.
-
-      For example:
-      ```nickel
-      1 | FailWith "message" =>
-        error: contract broken by a value: message
-          ┌─ :1:1
-          │
-        1 │ FailWith "message"
-          │ ---------------------------- expected type
-          │
-          ┌─ repl-input-0:1:1
-          │
-        1 │ 1 | FailWith "message"
-          │ ^ applied to this expression
-      ```
-    "%
+        A contract that always fails with the given message.
+
+        # Examples
+
+        ```nickel
+        1 | FailWith "message" =>
+          error: contract broken by a value: message
+        ```
+      "%
       = fun msg label value => contract.blame_with_message msg label,
 
     fail_with
       | String -> Dyn
       | doc m%"
-      Unconditionally abort evaluation with the given message.
-
-      For example:
-      ```nickel
-      fail_with "message" =>
-        error: contract broken by a value: message
-      ```
-    "%
+        Abort evaluation with the given message.
+
+        # Examples
+
+        ```nickel
+        fail_with "message" =>
+          error: contract broken by a value: message
+        ```
+      "%
       = fun msg => null | FailWith msg,
   },
 }
diff --git a/stdlib/contract.ncl b/stdlib/contract.ncl
index a2d5c971..9acf7094 100644
--- a/stdlib/contract.ncl
+++ b/stdlib/contract.ncl
@@ -1,5 +1,4 @@
-{
-  contract = {
+{ contract = {
     Equal
       | doc m%"
         `Equal` is a contract enforcing equality to a given constant.
@@ -19,129 +18,185 @@
          => ERROR (contract broken by a value)
         ```
       "%
-      = fun constant =>
-        %typeof% constant
-        |> match {
-          `Record =>
-            #TODO: lazy version for records. The difficulty is not to make it lazy
-            #right now, but to make it preserve recursivity as well. We might need
-            #something like a recursivity-preserving map, or a primop for
-            # per-field contract application, like %array_lazy_assume%.
-            from_predicate ((==) constant),
-          `Array =>
-            fun label' value =>
-              if %typeof% value == `Array then
-                let value_length = %length% value in
-                if value_length == %length% constant then
-                  %generate%
-                    value_length
-                    (
-                      fun i =>
-                        contract.apply (Equal (%elem_at% constant i)) label' value
-                    )
-                else
-                  label'
-                  |> label.with_message  "array length mismatch (expected `%{%to_str% (%length% constant)}`, got `%{%to_str% value_length}`)"
+    =
+      let fields_diff
+        | doc m%"
+            Compute the difference between the fields of two records.
+            `fields_diff` isn't concerned with the actual values themselves, but
+            just with field names.
+
+            Return a record of type
+            `{extra : Array String, missing: Array String}`, relative to the
+            first argument `constant`.
+          "%
+        = fun constant value =>
+        let diff = value
+          |> record.fields
+          |> array.fold_left (fun acc field =>
+            if record.has_field field acc.rest then
+              {
+                extra = acc.extra,
+                rest = record.remove field acc.rest,
+              }
+            else
+              {
+                extra = array.append field acc.extra,
+                rest = acc.rest,
+              }
+          ) { extra = [], rest = constant }
+        in
+        { extra = diff.extra, missing = record.fields diff.rest }
+      in
+
+      let blame_fields_differ = fun qualifier fields ctr_label =>
+        let plural = if %length% fields == 1 then "" else "s" in
+        ctr_label
+        |> label.with_message "%{qualifier} field%{plural} `%{string.join ", " fields}`"
+        |> label.append_note "`contract.Equal some_record` requires that the checked value is equal to the record `some_record`, but the sets of their fields differ."
+        |> blame
+      in
+
+      fun constant =>
+        let constant_type = %typeof% constant in
+        let check_typeof_eq = fun ctr_label value =>
+          let value_type = %typeof% value in
+          if value_type == constant_type then
+            value
+          else
+            ctr_label
+            |> label.with_message "expected `%{%to_str% constant_type}`, got `%{%to_str% value_type}`"
+            |> label.append_note "`contract.Equal some_value` requires that the checked value is equal to `some_value`, but they don't have the same type."
+            |> blame
+        in
 
-                  |> label.append_note "`Equal some_array` requires that the checked value is equal to the array `some_array`, but their lengths differ."
-                  |> blame
+        constant_type |> match {
+          `Record =>
+            # we map the constant from {field1 = val1, .., fieldn = valn} to
+            # {field1 = Equal val1, .., fieldn = Equal valn}, building a
+            # dictionary of equality contracts
+            let contract_map = %record_map% constant (fun _key => Equal) in
+            fun ctr_label value =>
+              let value = check_typeof_eq ctr_label value in
+              let diff = fields_diff constant value in
+
+              if %length% diff.extra != 0 then
+                blame_fields_differ "extra" diff.extra ctr_label
+              else if %length% diff.missing != 0 then
+                blame_fields_differ "missing" diff.missing ctr_label
               else
-                label'
-                |> label.with_message "expected Array, got %{%to_str% (%typeof% value)}"
+                %record_lazy_assume% ctr_label value (fun field =>
+                  contract_map."%{field}"),
 
-                |> label.append_note "`Equal some_array` requires that the checked value is equal to the array `some_array`, but the provided value isn't an array."
+          `Array =>
+            fun ctr_label value =>
+              let value = check_typeof_eq ctr_label value in
+              let value_length = %length% value in
+
+              if value_length == %length% constant then
+                %generate% value_length (fun i =>
+                  %elem_at% value i
+                  |> contract.apply (Equal (%elem_at% constant i)) ctr_label)
+              else
+                ctr_label
+                |> label.with_message "array length mismatch (expected `%{%to_str% (%length% constant)}`, got `%{%to_str% value_length})`"
+                |> label.append_note "`contract.Equal some_array` requires that the checked value is equal to the array `some_array`, but their lengths differ."
                 |> blame,
-          # Outside of lazy data structure, we just use (==)
-          _ => from_predicate ((==) constant),
+
+          # Outside of lazy data structures, we just use (==)
+          _ =>
+            fun ctr_label value =>
+              value
+              |> check_typeof_eq ctr_label
+              |> from_predicate ((==) constant) ctr_label,
         },
 
     blame
       | doc m%"
-        Raise blame for a given label.
+          Raises blame for a given label.
 
-        Type: `forall a. Lbl -> a`
-        (for technical reasons, this function isn't actually statically typed)
+          Type: `forall a. Label -> a`
+          (for technical reasons, this function isn't actually statically typed)
 
-        Blame is the mechanism to signal contract violiation in Nickel. It ends
-        the program execution and print a detailed report thanks to the
-        information tracked inside the label.
+          Blame is the mechanism to signal contract violation in Nickel. It
+          ends the program execution and prints a detailed report thanks to the
+          information tracked inside the label.
 
-        For example:
+          # Examples
 
-        ```nickel
-        IsZero = fun label value =>
-          if value == 0 then
-            value
-          else
-            contract.blame label
-        ```
+          ```nickel
+          IsZero = fun label value =>
+            if value == 0 then
+              value
+            else
+              contract.blame label
+          ```
         "%
       = fun label => %blame% label,
 
     blame_with_message
       | doc m%"
-        Raise blame with respect to a given label and a custom error message.
+          Raises blame with respect to a given label with a custom error message.
 
-        Type: `forall a. String -> Lbl -> a`
-        (for technical reasons, this function isn't actually statically typed)
+          Type: `forall a. String -> Label -> a`
+          (for technical reasons, this function isn't actually statically typed)
 
-        Same as `blame`, but take an additional custom error message that will be
-        displayed as part of the blame error. `blame_with_message message label`
-        is equivalent to `blame (label.with_message message label)`
+          Same as `blame`, but takes an additional error message that will be
+          displayed as part of the blame error. `blame_with_message message label`
+          is equivalent to `blame (label.with_message message label)`
 
-        For example:
+          # Examples
 
-        ```nickel
-        let IsZero = fun label value =>
-          if value == 0 then
-            value
-          else
-            contract.blame_with_message_message "Not zero" label
-        in
+          ```nickel
+          let IsZero = fun label value =>
+            if value == 0 then
+              value
+            else
+              contract.blame_with_message_message "Not zero" label
+          in
 
-        0 | IsZero
-        ```
+          0 | IsZero
+          ```
         "%
       = fun message label => %blame% (%label_with_message% message label),
 
     from_predicate
       | doc m%"
-        Generate a contract from a boolean predicate.
+          Generates a contract from a boolean predicate.
 
-        Type: `(Dyn -> Bool) -> (Lbl -> Dyn -> Dyn)`
-        (for technical reasons, this function isn't actually statically typed)
+          Type: `(Dyn -> Bool) -> (Label -> Dyn -> Dyn)`
+          (for technical reasons, this function isn't actually statically typed)
 
-        For example:
+          # Examples
 
-        ```nickel
-        let IsZero = contract.from_predicate (fun x => x == 0) in
-        0 | IsZero
-        ```
+          ```nickel
+          let IsZero = contract.from_predicate (fun x => x == 0) in
+          0 | IsZero
+          ```
         "%
       = fun pred label value => if pred value then value else %blame% label,
 
     label
       | doc m%"
-          The label submodule, which gathers functions that manipulate the label
-          of a contract.
+          The label submodule gathers functions that manipulate the label of
+          a contract.
 
           A label is a special opaque value automatically passed by the Nickel
           interpreter to contracts when performing a contract check.
 
           A label stores a stack of custom error diagnostics, that can be
-          manipulated by the function of this module. Labels thus offer a way to
-          customize the error message that will be shown if the contract is broken.
+          manipulated by the functions in this module. Labels thus offer a way to
+          customize the error message that will be shown if a contract is broken.
 
           The setters (`with_XXX` functions) always operate on the current error
-          diagnostic, which is the last diagnotic on the stack (if the stack is
-          empty, a fresh diagnostic is silently created when using a setter).
+          diagnostic, which is the last diagnotic on the stack. If the stack is
+          empty, a fresh diagnostic is silently created when using a setter.
           The previous diagnostics are thus archived, and can't be modified
           anymore. All diagnostics will be shown during error reporting, with
           the most recent being used as the main one.
 
           `contract.apply` is the operation that pushes a new fresh diagnostic on
-          the stack, saving the previously current error diagnostic. Indeed,
-          `contract.apply` is mostly used to apply subcontracts inside a parent
+          the stack, saving the previously current error diagnostic. The function
+          `contract.apply` is typically used to apply subcontracts inside a parent
           contract. Stacking the current diagnostic potentially customized by
           the parent contract saves the information inside, and provides a fresh
           diagnostic for the child contract to use.
@@ -149,86 +204,86 @@
       = {
         with_message
           | doc m%"
-            Attach a custom error message to the current error diagnostic of a
-            label.
-
-            Type: `String -> Lbl -> Lbl`
-            (for technical reasons, this function isn't actually statically typed)
-
-            If a custom error message was previously set, there are two
-            possibilities:
-              - the label has gone through a `contract.apply` call in-between.
-                In this case, the previous diagnostic has been stacked,
-                and using `with_message` won't erase anything but rather modify
-                the fresh current diagnostic.
-              - no `contract.apply` has taken place since the last message was
-                set. In this case, the current diagnostic is still the same, and
-                the previous error message will be erased.
-
-            For example:
-
-            ```nickel
-            let ContractNum = contract.from_predicate (fun x => x > 0 && x < 50) in
-
-            let Contract = fun label value =>
-              if builtin.is_number value then
-                contract.apply
-                  ContractNum
-                  (contract.label.with_message
-                    "num subcontract failed! (out of bound)"
-                    label
-                  )
+              Attaches a custom error message to the current error diagnostic of a
+              label.
+
+              Type: `String -> Label -> Label`
+              (for technical reasons, this function isn't actually statically typed)
+
+              If a custom error message was previously set, there are two
+              possibilities:
+                - the label has gone through a `contract.apply` call in-between.
+                  In this case, the previous diagnostic has been stacked,
+                  and using `with_message` won't erase anything but rather modify
+                  the fresh current diagnostic.
+                - no `contract.apply` has taken place since the last message
+                  was set. In this case, the current diagnostic's error message
+                  is replaced.
+
+              # Examples
+
+              ```nickel
+              let ContractNum = contract.from_predicate (fun x => x > 0 && x < 50) in
+
+              let Contract = fun label value =>
+                if builtin.is_number value then
+                  contract.apply
+                    ContractNum
+                    (contract.label.with_message
+                      "num subcontract failed! (out of bound)"
+                      label
+                    )
+                    value
+                else
                   value
-              else
-                value
-            in
+              in
 
-            5 | Contract
-            ```
+              5 | Contract
+              ```
             "%
           = fun message label => %label_with_message% message label,
 
         with_notes
           | doc m%"
-            Attach custom error notes to the current error diagnostic of a
-            label.
-
-            Type: `Array String -> Lbl -> Lbl`
-            (for technical reasons, this function isn't actually statically typed)
-
-            If custom error notes were previously set, there are two
-            possibilities:
-              - the label has gone through a `contract.apply` call in-between.
-                In this case, the previous diagnostic has been stacked,
-                and using `with_notes` won't erase anything but rather modify
-                the fresh current diagnostic.
-              - no `contract.apply` has taken place since the last message was
-                set. In this case, the current diagnostic is still the same, and
-                the previous error notes will be erased.
-
-            For example:
-
-            ```nickel
-            let ContractNum = contract.from_predicate (fun x => x > 0 && x < 50) in
-
-            let Contract = fun label value =>
-              if builtin.is_number value then
-                contract.apply
-                  ContractNum
-                  (label
-                   |> contract.label.with_message "num subcontract failed! (out of bound)"
-                   |> contract.label.with_notes [
-                        "The value was a number, but this number is out of the expected bound",
-                        "The value must be a number between 0 and 50, both excluded",
-                      ]
-                  )
+              Attaches custom error notes to the current error diagnostic of a
+              label.
+
+              Type: `Array String -> Label -> Label`
+              (for technical reasons, this function isn't actually statically typed)
+
+              If custom error notes were previously set, there are two
+              possibilities:
+                - the label has gone through a `contract.apply` call in-between.
+                  In this case, the previous diagnostic has been stacked,
+                  and using `with_notes` won't erase anything but rather modify
+                  the fresh current diagnostic.
+                - no `contract.apply` has taken place since the last message was
+                  set. In this case, the current diagnostic's error notes will
+                  be replaced.
+
+              # Examples
+
+              ```nickel
+              let ContractNum = contract.from_predicate (fun x => x > 0 && x < 50) in
+
+              let Contract = fun label value =>
+                if builtin.is_number value then
+                  contract.apply
+                    ContractNum
+                    (label
+                     |> contract.label.with_message "num subcontract failed! (out of bound)"
+                     |> contract.label.with_notes [
+                          "The value was a number, but this number is outside the expected bounds",
+                          "The value must be a number between 0 and 50, both excluded",
+                        ]
+                    )
+                    value
+                else
                   value
-              else
-                value
-            in
+              in
 
-            5 | Contract
-            ```
+              5 | Contract
+              ```
             "%
           # the %label_with_notes% operator expects an array of strings which is
           # fully evaluated, thus we force the notes first
@@ -236,9 +291,9 @@
 
         append_note
           | doc m%"
-              Append a note to the notes of the current diagnostic of a label.
+              Appends a note to the error notes of the current diagnostic of a label.
 
-              Type: `String -> Lbl -> Lbl`
+              Type: `String -> Label -> Label`
               (for technical reasons, this function isn't actually statically typed)
             "%
           = fun note label => %label_append_note% note label,
@@ -246,18 +301,18 @@
 
     apply
       | doc m%"
-          Apply a contract to a label and a value.
+          Applies a contract to a label and a value.
 
-          Type: `Contract -> Lbl -> Dyn -> Dyn`
+          Type: `Contract -> Label -> Dyn -> Dyn`
           (for technical reasons, this function isn't actually statically typed)
 
           Nickel supports user-defined contracts defined as functions, but also
-          as records. Moreover, the interpreter performs additional book-keeping
+          as records. Moreover, the interpreter performs additional bookkeeping
           for error reporting when applying a contract in an expression
           `value | Contract`. You should not use standard function application
           to apply a contract, but this function instead.
 
-          # Example
+          # Examples
 
           ```nickel
           let Nullable = fun param_contract label value =>
@@ -268,13 +323,13 @@
           ({foo = 1} | Contract)
           ```
 
-          # Diagnostic stack
+          # Diagnostics stack
 
-          Using `apply` will stack the current custom reporting data, and create a
-          fresh current working diagnostic. `apply` thus acts automatically as a
-          split point between a contract and its subcontracts, providing the
-          subcontracts with a fresh diagnostic to use, while remembering the
-          previous diagnostics set by parent contracts.
+          Using `apply` will stack the current custom error reporting data, and
+          create a fresh working diagnostic. `apply` thus acts as a split point
+          between a contract and its subcontracts, providing the subcontracts
+          with a fresh diagnostic to use, while remembering the previous
+          diagnostics set by parent contracts.
 
           ## Illustration
 
diff --git a/stdlib/enum.ncl b/stdlib/enum.ncl
index c61c9e15..a6c045d5 100644
--- a/stdlib/enum.ncl
+++ b/stdlib/enum.ncl
@@ -2,53 +2,53 @@
   enum = {
     Tag
       | doc m%"
-          Contract to enforce the value is an enum tag.
+        Enforces that the value is an enum tag.
 
-          # Examples
+        # Examples
 
-          ```nickel
-            (`foo | Tag) =>
-              `foo
-            (`FooBar | Tag) =>
-              `FooBar
-            ("tag" | Tag) =>
-              error
-          ```
-          "%
+        ```nickel
+        (`foo | Tag) =>
+          `foo
+        (`FooBar | Tag) =>
+          `FooBar
+        ("tag" | Tag) =>
+          error
+        ```
+      "%
       = contract.from_predicate builtin.is_enum,
 
     TagOrString
       | doc m%%"
-            Accepts both enum tags and strings. Strings are automatically
-            converted to an enum tag.
+        Accepts both enum tags and strings. Strings are automatically
+        converted to an enum tag.
 
-            `TagOrString` is typically used in conjunction with an enum type, to
-            accept tags represented as strings (e.g. coming from a JSON
-            serialization) as well.
+        `TagOrString` is typically used in conjunction with an enum type, to
+        accept tags represented as strings (e.g. coming from a JSON
+        serialization) as well.
 
-            # Examples
+        # Examples
 
-            ``` nickel
-            let Schema = {
-              protocol
-                | enum.TagOrString
-                | [| `http, `ftp |],
-              port
-                | Number,
-              method
-                | enum.TagOrString
-                | [| `GET, `POST |]
-            } in
-            let serialized =
-              m%"
-                {"protocol": "http", "port": 80, "method": "GET"}
-              "%
-              |> builtin.deserialize `Json
-            in
+        ``` nickel
+        let Schema = {
+          protocol
+            | enum.TagOrString
+            | [| `http, `ftp |],
+          port
+            | Number,
+          method
+            | enum.TagOrString
+            | [| `GET, `POST |]
+        } in
+        let serialized =
+          m%"
+            {"protocol": "http", "port": 80, "method": "GET"}
+          "%
+          |> builtin.deserialize `Json
+        in
 
-            serialized | Schema
-            ```
-          "%%
+        serialized | Schema
+        ```
+      "%%
       = fun label value =>
         %typeof% value
         |> match {
diff --git a/stdlib/function.ncl b/stdlib/function.ncl
index aa2715be..39bfd5a4 100644
--- a/stdlib/function.ncl
+++ b/stdlib/function.ncl
@@ -3,80 +3,94 @@
     id
       : forall a. a -> a
       | doc m%"
-    The identity function, that is
-    ```nickel
-    id x == x
-    ```
-    for any value `x`.
-    "%
+        The identity function, that is
+
+        ```nickel
+        id x = x
+        ```
+
+        for any value `x`.
+      "%
       = fun x => x,
 
     const
       : forall a b. a -> b -> a
       | doc m%"
-    Results in the first argument.
+        Always returns the first argument, ignoring the second.
+
+        # Examples
 
-    For example:
-    ```nickel
-    const 5 42 == 5
-    ```
-    "%
+        ```nickel
+        const 5 42 == 5
+        ```
+      "%
       = fun x y => x,
 
     compose
       : forall a b c. (b -> c) -> (a -> b) -> (a -> c)
       | doc m%"
-    Function composition, right to left. That is,
-    ```nickel
-    compose f g x == f (g x)
-    ```
-    "%
+        Function composition, from right to left. That is,
+
+        ```nickel
+        compose f g x == f (g x)
+        ```
+      "%
       = fun g f x => x |> f |> g,
 
     flip
       : forall a b c. (a -> b -> c) -> b -> a -> c
       | doc m%%"
-    Flip the argument order for a two-argument function. For example,
-    ```nickel
-    flip (fun x y => "%{x} %{y}") "world!" "Hello,"
-      => "Hello, world!"
-    ```
-    "%%
+        Flips the argument order for a two-argument function.
+
+        # Examples
+
+        ```nickel
+        flip (fun x y => "%{x} %{y}") "world!" "Hello,"
+          => "Hello, world!"
+        ```
+      "%%
       = fun f x y => f y x,
 
     first
       : forall a b. a -> b -> a
       | doc m%"
-    Always returns the first argument:
-    ```nickel
-    first 5 7 => 5
-    ```
-    "%
+        Always returns the first argument, ignoring the second.
+
+        # Examples
+
+        ```nickel
+        first 5 7 => 5
+        ```
+      "%
       = const,
 
     second
       : forall a b. a -> b -> b
       | doc m%"
-    Always returns the second argument:
-    ```nickel
-    second 5 7 => 7
-    ```
-    "%
+        Always returns the second argument, ignoring the first.
+
+        # Examples
+
+        ```nickel
+        second 5 7 => 7
+        ```
+      "%
       = flip const,
 
     pipe
       : forall a. a -> Array (a -> a) -> a
       | doc m%%"
-      Apply an array of functions to a value, in order.
-
-      For example:
-      ```nickel
-      pipe 2 [ (+) 2, (+) 3 ]
-        => 7
-      pipe `World [ string.from, fun s => "Hello, %{s}!" ]
-        => "Hello, World!"
-      ```
-    "%%
+        Applies an array of functions to a value, in order.
+
+        # Examples
+
+        ```nickel
+        pipe 2 [ (+) 2, (+) 3 ]
+          => 7
+        pipe `World [ string.from, fun s => "Hello, %{s}!" ]
+          => "Hello, World!"
+        ```
+      "%%
       = fun x fs => array.fold_left (|>) x fs,
   }
 }
diff --git a/stdlib/internals.ncl b/stdlib/internals.ncl
index daf6d273..f42cca3a 100644
--- a/stdlib/internals.ncl
+++ b/stdlib/internals.ncl
@@ -58,8 +58,8 @@
 
   "$record" = fun field_contracts tail_contract label value =>
     if %typeof% value == `Record then
-      # Returns the sub-record of `l` containing only those fields which are not
-      # present in `r`. If `l` has a sealed polymorphic tail then it will be
+      # Returns the sub-record of `left` containing only those fields which are not
+      # present in `right`. If `left` has a sealed polymorphic tail then it will be
       # preserved.
       let field_diff =
         fun left right =>
diff --git a/stdlib/number.ncl b/stdlib/number.ncl
index 9515c142..0b7942e0 100644
--- a/stdlib/number.ncl
+++ b/stdlib/number.ncl
@@ -2,15 +2,16 @@
   number = {
     Integer
       | doc m%"
-      Contract to enforce a number is an integer.
+        Enforces that a number is an integer.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         (1.5 | Integer) =>
           error
         (42 | Integer) =>
           42
-      ```
+        ```
       "%
       = fun label value =>
         if %typeof% value == `Number then
@@ -23,17 +24,18 @@
 
     Nat
       | doc m%"
-      Contract to enforce a number is a natural number (including 0).
+        Enforces that a number is a natural number (including 0).
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         (42 | Nat) =>
           42
         (0 | Nat) =>
           0
         (-4 | Nat) =>
           error
-      ```
+        ```
       "%
       = fun label value =>
         if %typeof% value == `Number then
@@ -46,17 +48,18 @@
 
     PosNat
       | doc m%"
-      Contract to enforce a number is a positive natural number.
+        Enforces that a number is a strictly positive natural number.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         (42 | PosNat) =>
           42
         (0 | PosNat) =>
           error
         (-4 | PosNat) =>
           error
-      ```
+        ```
       "%
       = fun label value =>
         if %typeof% value == `Number then
@@ -69,15 +72,16 @@
 
     NonZero
       | doc m%"
-      Contract to enforce a number is anything but zero.
+        Enforces that a number is not equal to zero.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         (1 | NonZero) =>
           1
         (0.0 | NonZero) =>
           error
-      ```
+        ```
       "%
       = fun label value =>
         if %typeof% value == `Number then
@@ -91,54 +95,60 @@
     is_integer
       : Number -> Bool
       | doc m%"
-      Checks if the given number is an integer.
+        Checks if the given number is an integer.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         is_int 42 =>
           true
         is_int 1.5 =>
           false
-      ```
+        ```
       "%
       = fun x => x % 1 == 0,
 
     min
       : Number -> Number -> Number
       | doc m%"
-      Results in the lowest of the given two numbers.
+        Returns the lower of two numbers.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         min (-1337) 42 =>
           -1337
-      ```
+        ```
       "%
       = fun x y => if x <= y then x else y,
 
     max
       : Number -> Number -> Number
       | doc m%"
-      Results in the highest of the given two numbers.
+        Returns the higher of two numbers.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         max (-1337) 42 =>
           42
-      ```
+        ```
       "%
       = fun x y => if x >= y then x else y,
 
     floor
       : Number -> Number
       | doc m%"
-      Rounds the number down to the next integer.
+        Rounds a number down to the next integer.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         floor 42.5 =>
           42
-      ```
+        floor (-42.5) =>
+          -43
+        ```
       "%
       = fun x =>
         if x >= 0 then
@@ -149,71 +159,75 @@
     abs
       : Number -> Number
       | doc m%"
-      Results in the absolute value of the given number.
+        Returns the absolute value of a number.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         abs (-5) =>
           5
         abs 42 =>
           42
-      ```
+        ```
       "%
       = fun x => if x < 0 then -x else x,
 
     fract
       : Number -> Number
       | doc m%"
-      Results in the fractional part of the given number.
+        Returns the fractional part of a number.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         fract 13.37 =>
           0.37
         fract 42 =>
           0
-      ```
+        ```
       "%
       = fun x => x % 1,
 
     truncate
       : Number -> Number
       | doc m%"
-      Truncates the given number.
+        Truncates a number, rounding it towards 0.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         truncate (-13.37) =>
           -13
         truncate 42.5 =>
           42
-      ```
+        ```
       "%
       = fun x => x - (x % 1),
 
     pow
       : Number -> Number -> Number
       | doc m%"
-      `pow x y` results in `x` to the power of `y`.
+        `pow x y` returns `x` to the power of `y`.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         pow 2 8 =>
           256
-      ```
+        ```
 
-      # Precision
+        # Precision
 
-      Nickel numbers are arbitrary precision rationals. If the exponent `y` is
-      an integer which fits in a 64-bits signed or unsigned integer (that is, if
-      `y` is an integer between `−2^63` and `2^64-1`), the result is computed
-      exactly.
+        Nickel numbers are arbitrary precision rationals. If the exponent `y` is
+        an integer which fits into a 64-bit signed or unsigned integer (that is, if
+        `y` is an integer between `−2^63` and `2^64-1`), the result is computed
+        exactly.
 
-      Otherwise, both operands `x` and `y` are converted to the nearest 64 bits
-      float (excluding `NaN` and infinity), and we compute the result as a 64
-      bits float. This result is then converted back to a rational. In this
-      case, **be aware that both the conversion from rationals to floats, and
-      the power operation, might incur rounding errors**.
+        Otherwise, both operands `x` and `y` are converted to the nearest 64 bit
+        float (excluding `NaN` and infinity), and we compute the result as a 64
+        bit float. This result is then converted back to a rational. In this
+        case, **be aware that both the conversion from rationals to floats, and
+        the power operation, might incur rounding errors**.
       "%
       = fun x n => %pow% x n,
   }
diff --git a/stdlib/record.ncl b/stdlib/record.ncl
index dd3c0a7f..3da262c6 100644
--- a/stdlib/record.ncl
+++ b/stdlib/record.ncl
@@ -3,111 +3,127 @@
     map
       : forall a b. (String -> a -> b) -> { _ : a } -> { _ : b }
       | doc m%"
-      Maps a function on every field of a record. The string argument of the function argument is the name of the
-      field.
+        Maps a function over every field of a record. The function is passed the
+        name and value of each field.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         map (fun s x => s) { hi = 2 } =>
           { hi = "hi" }
         map (fun s x => x + 1) { hello = 1, world = 2 } =>
           { hello = 2, world = 3 }
-      ```
+        ```
       "%
       = fun f r => %record_map% r f,
 
     fields
       : forall a. { _ : a } -> Array String
       | doc m%"
-      Given a record, results in a array of the string representation of all fields in the record.
+        Returns an array containing the names of all the fields of a record.
+
+        # Examples
 
-      ```nickel
+        ```nickel
         fields { one = 1, two = 2 } =>
           [ "one", "two" ]
-      ```
+        ```
       "%
       = fun r => %fields% r,
 
     values
       : forall a. { _ : a } -> Array a
       | doc m%"
-      Given a record, results in a array containing all the values in that record.
+        Returns an array containing the values of all the fields of a record.
+
+        # Examples
 
-      ```nickel
+        ```nickel
         values { one = 1, world = "world" }
           [ 1, "world" ]
-      ```
+        ```
       "%
       = fun r => %values% r,
 
     has_field
       : forall a. String -> { _ : a } -> Bool
       | doc m%"
-      Given the name of a field and a record, checks if the record contains the given field.
+        Given a string, checks if a record contains a field of that name.
 
-      ```nickel
+        # Examples
+
+        ```nickel
         has_field "hello" { one = 1, two = 2 } =>
           false
         has_field "one" { one = 1, two = 2 } =>
           true
-      ```
+        ```
       "%
       = fun field r => %has_field% field r,
 
     insert
       : forall a. String -> a -> { _ : a } -> { _ : a }
       | doc m%%"
-        Insert a new field in a record. `insert` doesn't mutate the original
-        record but returns a new one instead.
-
-        ```nickel
-        insert "foo" foo { bar = "bar" } =>
-          { foo = "foo", bar = "bar }
-
-        {}
-        |> insert "file.%{ext}" "data/text"
-        |> insert "length" 10*1000 =>
-          {"file.txt" = "data/text", "length" = 10000}
-        ```
-      "%%
+          Inserts a new field into a record. `insert` doesn't mutate the original
+          record but returns a new one instead.
+
+          # Examples
+
+          ```nickel
+          insert "foo" 5 { bar = "bar" } =>
+            { foo = 5, bar = "bar }
+          {}
+          |> insert "file.%{ext}" "data/text"
+          |> insert "length" 10*1000 =>
+            {"file.txt" = "data/text", "length" = 10000}
+          ```
+        "%%
       = fun field content r => %record_insert% field r content,
 
     remove
       : forall a. String -> { _ : a } -> { _ : a }
       | doc m%"
-        Remove a field from a record. `remove` doesn't mutate the original
-        record but returns a new one instead.
+          Removes a field from a record. `remove` doesn't mutate the original
+          record but returns a new one instead.
 
-        ```nickel
-        remove "foo" foo { foo = "foo", bar = "bar" } =>
-          { bar = "bar }
-        ```
-      "%
+          # Examples
+
+          ```nickel
+          remove "foo" { foo = "foo", bar = "bar" } =>
+            { bar = "bar }
+          ```
+        "%
       = fun field r => %record_remove% field r,
 
     update
       : forall a. String -> a -> { _ : a } -> { _ : a }
       | doc m%"
-        Update a field of a record with a new value. `update` doesn't mutate the
-        original record but returns a new one instead. If the field to update is absent
-        from the given record, `update` simply adds it.
-
-        ```nickel
-        remove "foo" foo { foo = "foo", bar = "bar" } =>
-          { bar = "bar" }
-        ```
-
-        As opposed to overriding a value with the merge operator `&`, `update`
-        will only change the specified field and won't automatically update the other
-        fields which depend on it:
-
-        ```nickel
-        { foo = bar + 1, bar | default = 0 } & { bar = 1 } =>
-          { foo = 2, bar = 1 }
-        update "bar" 1 {foo = bar + 1, bar | default = 0 } =>
-          { foo = 1, bar = 1 }
-        ```
-      "%
+          Updates a field of a record with a new value. `update` doesn't mutate the
+          original record but returns a new one instead. If the field to update is absent
+          from the given record, `update` adds it.
+
+          # Examples
+
+          ```nickel
+          update "foo" 5 { foo = "foo", bar = "bar" } =>
+            { foo = 5, bar = "bar" }
+          update "foo" 5 { bar = "bar" } =>
+            { foo = 5, bar = "bar" }
+          ```
+
+          # Overriding
+
+          As opposed to overriding a value with the merge operator `&`, `update`
+          will only change the specified field and won't automatically update the other
+          fields which depend on it:
+
+          ```nickel
+          { foo = bar + 1, bar | default = 0 } & { bar = 1 } =>
+            { foo = 2, bar = 1 }
+          update "bar" 1 {foo = bar + 1, bar | default = 0 } =>
+            { foo = 1, bar = 1 }
+          ```
+        "%
       = fun field content r =>
         let r =
           if %has_field% field r then
@@ -120,31 +136,33 @@
     map_values
       : forall a b. (a -> b) -> { _ : a } -> { _ : b }
       | doc m%"
-      Maps a function over every field value of a record.
+        Maps a function over the values of all the fields of a record.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         map_values (fun x => x + 1) { hi = 2 } =>
           { hi = 3 }
         map (fun x => x + 1) { hello = 1, world = 2 } =>
           { hello = 2, world = 3 }
-      ```
-    "%
+        ```
+      "%
       = fun f => map (fun _field value => f value),
 
     to_array
       : forall a. { _ : a } -> Array { field : String, value : a }
       | doc m%"
-      Converts a record to an array of key-value pairs.
+        Converts a record to an array of key-value pairs.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         to_array { hello = "world", foo = "bar" } =>
           [ { field = "hello", value = "world" },
             { field = "foo", value = "bar" }
           ]
-      ```
-    "%
+        ```
+      "%
       = fun record =>
         record
         |> fields
@@ -153,16 +171,17 @@
     from_array
       : forall a. Array { field : String, value : a } -> { _ : a }
       | doc m%"
-      Convert an array of key-value pairs into a record.
+        Converts an array of key-value pairs into a record.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         from_array [ { field = "hello", value = "world" },
                      { field = "foo", value = "bar" }
                    ]
           => { hello = "world", foo = "bar" }
-      ```
-    "%
+        ```
+      "%
       = fun bindings =>
         bindings
         |> array.map (fun binding => { "%{binding.field}" = binding.value })
@@ -171,40 +190,45 @@
     is_empty
       : forall a. { _ : a } -> Bool
       | doc m%"
-      Check whether a record is empty.
+        Checks whether a record is empty.
 
-      ```nickel
-      is_empty {} => true
-      is_empty { foo = 1 } => false
-      ```
-    "%
+        # Examples
+
+        ```nickel
+        is_empty {} => true
+        is_empty { foo = 1 } => false
+        ```
+      "%
       = (==) {},
 
     merge_all
       | forall a. Array { _ : a } -> { _ : a }
       | doc m%"
-      Merge an array of records.
-
-      For example:
-      ```nickel
-      merge_array [ { foo = 1 }, { bar = 2 } ]
-        => { foo = 1, bar = 2 }
-      ```
-    "%
+        Merges an array of records.
+
+        # Examples
+
+        ```nickel
+        merge_array [ { foo = 1 }, { bar = 2 } ]
+          => { foo = 1, bar = 2 }
+        ```
+      "%
       = array.fold_left (&) {},
 
     filter
       : forall a. (String -> a -> Bool) -> { _ : a } -> { _ : a }
       | doc m%"
-      Filter a record using the given function, which is passed the name and
-      value for each key to make a decision.
-
-      For example:
-      ```nickel
-      filter (fun name x => x % 2 == 0) { even = 2, odd = 3 }
-        => { even = 2 }
-      ```
-    "%
+        `filter f r` returns a record containing all fields from `r` for which
+        `f` returns true. The function `f` is passed the name and value of each field
+        to make a decision.
+
+        # Examples
+
+        ```nickel
+        filter (fun name x => x % 2 == 0) { even = 2, odd = 3 }
+          => { even = 2 }
+        ```
+      "%
       = fun f record =>
         record
         |> to_array
diff --git a/stdlib/string.ncl b/stdlib/string.ncl
index aa31fd92..1f676ac1 100644
--- a/stdlib/string.ncl
+++ b/stdlib/string.ncl
@@ -2,18 +2,21 @@
   string = {
     BoolLiteral
       | doc m%"
-      Contract to enforce the value is a string that represents a boolean literal. Additionally casts "True" to "true"
-      and "False" to "false".
+        Enforces that the value is a string that represents a boolean
+        literal, namely
+        - "True" or "true", which will be normalized to "true"; or
+        - "False" or "false", which will be normalized to "false".
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         ("True" | BoolLiteral) =>
           "true"
         ("hello" | BoolLiteral) =>
           error
         (true | BoolLiteral) =>
           error
-      ```
+        ```
       "%
       = fun l s =>
         if %typeof% s == `String then
@@ -26,19 +29,20 @@
         else
           %blame% (%label_with_message% "not a string" l),
 
-    NumLiteral
+    NumberLiteral
       | doc m%"
-      Contract to enforce the value is a string that represends a numerical value.
+        Enforces that the value is a string that represents a numerical value.
 
-      For example:
-      ```nickel
-        ("+1.2" | NumLiteral) =>
+        # Examples
+
+        ```nickel
+        ("+1.2" | NumberLiteral) =>
           "+1.2"
-        ("5" | NumLiteral) =>
+        ("5" | NumberLiteral) =>
           "5"
-        (42 | NumLiteral) =>
+        (42 | NumberLiteral) =>
           error
-      ```
+        ```
       "%
       = let pattern = m%"^[+-]?(\d+(\.\d*)?(e[+-]?\d+)?|\.\d+(e[+-]?\d+)?)$"% in
       let is_num_literal = %str_is_match% pattern in
@@ -47,16 +51,17 @@
           if is_num_literal s then
             s
           else
-            %blame% (%label_with_message% "invalid num literal" l)
+            %blame% (%label_with_message% "invalid number literal" l)
         else
           %blame% (%label_with_message% "not a string" l),
 
     Character
       | doc m%"
-      Contract to enforce the value is a character (i.e. a string of length 1).
+        Enforces that the value is a character, i.e. a string of length 1.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         ("e" | Character) =>
           "e"
         ("#" | Character) =>
@@ -65,7 +70,7 @@
           error
         (1 | Character) =>
           error
-      ```
+        ```
       "%
       = fun l s =>
         if %typeof% s == `String then
@@ -78,17 +83,18 @@
 
     Stringable
       | doc m%"
-      Contract to enforce the value is convertible to a string via
-      `builtin.to_string` or `string.from`. Accepted values are:
+        Enforces that the value is convertible to a string via
+        `builtin.to_string` or `string.from`. Accepted values are:
 
-      - numbers
-      - booleans
-      - strings
-      - enum tags
-      - null
+        - numbers
+        - booleans
+        - strings
+        - enum tags
+        - null
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         (`Foo | Stringable) =>
           `Foo
         (false | Stringable) =>
@@ -97,7 +103,7 @@
           "barfoo"
         ({foo = "baz"} | Stringable) =>
           error
-      ```
+        ```
       "%
       = contract.from_predicate
         (
@@ -112,17 +118,18 @@
 
     NonEmpty
       | doc m%"
-      Contract to enforce the value is a non-empty string.
+        Enforces that the value is a non-empty string.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         ("" | NonEmpty) =>
           error
         ("hi!" | NonEmpty) =>
           "hi!"
         (42 | NonEmpty) =>
           error
-      ```
+        ```
       "%
       = fun l s =>
         if %typeof% s == `String then
@@ -136,13 +143,14 @@
     join
       : String -> Array String -> String
       | doc m%"
-      Joins a array of strings given a separator.
+        Joins an array of strings with a given separator.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         join ", " [ "Hello", "World!" ] =>
           "Hello, World!"
-      ```
+        ```
       "%
       = fun sep fragments =>
         let length = %length% fragments in
@@ -159,120 +167,127 @@
     split
       : String -> String -> Array String
       | doc m%"
-      Splits a string based on a separator string.
-
-      For example:
-      ```nickel
-      split "," "1,2,3" =>
-        [ "1", "2", "3" ]
-      split "." "1,2,3" =>
-        [ "1,2,3" ]
-      ```
+        Splits a string based on a separator string.
+
+        Note that this function never splits up Unicode extended grapheme
+        clusters, even in cases where the sought value exists within one.
 
-      Note that this function never splits up Unicode extended grapheme
-      clusters, even in cases where the sought value exists within one.
+        # Examples
+
+        ```nickel
+        split "," "1,2,3" =>
+          [ "1", "2", "3" ]
+        split "." "1,2,3" =>
+          [ "1,2,3" ]
+        ```
       "%
       = fun sep s => %str_split% s sep,
 
     trim
       : String -> String
       | doc m%"
-      Trims whitespace from the start and end of the string.
-
-      For example:
-      ```nickel
-      trim " hi  " =>
-        "hi"
-      trim "1   2   3   " =>
-        "1   2   3"
-      ```
+        Trims whitespace from the start and end of a string.
+
+        # Examples
+
+        ```nickel
+        trim " hi  " =>
+          "hi"
+        trim "1   2   3   " =>
+          "1   2   3"
+        ```
       "%
       = fun s => %str_trim% s,
 
     characters
       : String -> Array String
       | doc m%"
-      Separates a string into its individual Unicode extended grapheme clusters.
+        Separates a string into its individual Unicode extended grapheme clusters.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         chars "Hello" =>
           [ "H", "e", "l", "l", "o" ]
-      ```
+        ```
       "%
       = fun s => %str_chars% s,
 
     uppercase
       : String -> String
       | doc m%"
-      Results in the uppercase version of the given character (including non-ascii characters) if it exists, the same
-      character if not.
+        Returns the uppercase version of the given character (including non-ascii characters) if it exists, the same
+        character if not.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         uppercase "a" =>
           "A"
         uppercase "æ" =>
           "Æ"
         uppercase "." =>
           "."
-      ```
+        ```
       "%
       = fun s => %str_uppercase% s,
 
     lowercase
       : String -> String
       | doc m%"
-      Results in the lowercase version of the given character (including non-ascii characters) if it exists, the same
-      character if not.
+        Returns the lowercase version of the given character (including non-ascii characters) if it exists, the same
+        character if not.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         lowercase "A" =>
           "a"
         lowercase "Æ" =>
           "æ"
         lowercase "." =>
           "."
-      ```
+        ```
       "%
       = fun s => %str_lowercase% s,
 
     contains
       : String -> String -> Bool
       | doc m%"
-      Checks if the first string is part of the second string.
+        Checks if the first string is part of the second string.
 
-      For example:
-      ```nickel
+        Note that this function returns false if the sought string exists entirely inside
+        or straddles Unicode extended grapheme clusters.
+
+        # Examples
+
+        ```nickel
         contains "cde" "abcdef" =>
           true
         contains "" "abcdef" =>
           true
         contains "ghj" "abcdef" =>
           false
-      ```
-
-      Note that this function returns false if the sought string exists inside
-      a Unicode extended grapheme cluster.
+        ```
       "%
       = fun subs s => %str_contains% s subs,
 
     replace
       : String -> String -> String -> String
       | doc m%"
-      `replace sub repl str` replaces every occurrence of `sub` in `str` with `repl`.
+        `replace sub repl str` replaces every occurrence of `sub` in `str` with `repl`.
 
-      For example:
-      ```nickel
+        Note that this function will not replace `sub` if it exists within a
+        larger unicode extended grapheme cluster.
+
+        # Examples
+
+        ```nickel
         replace "cd" "   " "abcdef" =>
           "ab   ef"
         replace "" "A" "abcdef" =>
           "AaAbAcAdAeAfA"
-      ```
-
-      Note that this function will not replace `sub` if it exists within a
-      larger unicode extended grapheme cluster.
+        ```
       "%
       = fun pattern replace s =>
         %str_replace% s pattern replace,
@@ -280,19 +295,20 @@
     replace_regex
       : String -> String -> String -> String
       | doc m%"
-      `replace_regex regex repl str` replaces every match of `regex` in `str` with `repl`.
+        `replace_regex regex repl string` replaces every match of `regex` in `string` with `repl`.
+
+        **WARNING**: this function will currently break up Unicode extended
+        grapheme clusters if the regex matches a codepoint that exists within
+        one. This behaviour is expected to change in a future version.
 
-      For example:
-      ```nickel
+        # Examples
+
+        ```nickel
         replace_regex "l+." "j" "Hello!" =>
           "Hej!"
         replace_regex "\\d+" "\"a\" is not" "This 37 is a number." =>
           "This \"a\" is not a number."
       ```
-
-      **WARNING**: this function will currently break up Unicode extended
-      grapheme clusters if the regex matches a codepoint that exists within
-      one. This behaviour is expected to change in a future version.
       "%
       = fun pattern replace s =>
         %str_replace_regex% s pattern replace,
@@ -300,78 +316,92 @@
     is_match
       : String -> String -> Bool
       | doc m%"
-      `is_match regex String` checks if `String` matches `regex`.
+        `is_match regex string` checks if `string` matches `regex`.
 
-      For example:
-      ```nickel
+        **WARNING**: this function will currently return `true` even if the
+        regex match overlaps, or happens entirely within, with a Unicode extended
+        grapheme cluster. This behaviour is expected to change in a future version.
+
+        # Examples
+
+        ```nickel
         is_match "^\\d+$" "123" =>
           true
         is_match "\\d{4}" "123" =>
           false
-      ```
+        ```
 
-      For example, in the following program, the whole call to
-      `string.is_match "[0-9]*\\.?[0-9]+ x"` is re-evaluated at each invocation of
-      `is_number`. The regexp will be recompiled 3 times in total:
+        # Performance
 
-      ```nickel
-      let is_number = fun x => string.is_match "[0-9]*\\.?[0-9]+" x in
-      ["0", "42", "0.5"] |> array.all is_number =>
-        true
-      ```
+        When checking multiple strings against a common regular expression, it
+        is advantageous to store a partially applied version of this function. This
+        partial evaluation will store a compiled version of the regular expression and
+        prevent recompilation at each call site.
 
-      On the other hand, in the version below, the partial application of
-      `string.is_match "[0-9]*\\.?[0-9]+"` is evaluated once, returning a
-      function capturing the compiled regexp. The regexp will only be compiled
-      once and for all:
+        For example, in the following program, the whole call to
+        `string.is_match "[0-9]*\\.?[0-9]+ x"` is re-evaluated at each invocation of
+        `is_number`. The regexp will be recompiled 3 times in total:
 
-      ```nickel
-      let is_number' = string.is_match "[0-9]*\\.?[0-9]+" in
-      ["0", "42", "0.5"] |> array.all is_number' =>
-        true
-      ```
+        ```nickel
+        let is_number = fun x => string.is_match "[0-9]*\\.?[0-9]+" x in
+        ["0", "42", "0.5"] |> array.all is_number =>
+          true
+        ```
 
-      **WARNING**: this function will currently return `true` even if the
-      regex match overlaps, or happens entirely within, with a Unicode extended
-      grapheme cluster. This behaviour is expected to change in a future version.
+        On the other hand, in the version below, the partial application of
+        `string.is_match "[0-9]*\\.?[0-9]+"` is evaluated once, returning a
+        function capturing the compiled regexp. The regexp will only be compiled
+        once and for all:
+
+        ```nickel
+        let is_number' = string.is_match "[0-9]*\\.?[0-9]+" in
+        ["0", "42", "0.5"] |> array.all is_number' =>
+          true
+        ```
       "%
       = fun regex => %str_is_match% regex,
 
     find
       : String -> String -> { matched : String, index : Number, groups : Array String }
       | doc m%"
-          `find regex str` matches `str` given `regex`. Results in the part of `str` that matched, the index of the
-          first character that was part of the match in `str`, and a arrays of all capture groups if any.
-
-          For example:
-          ```nickel
-            find "^(\\d).*(\\d).*(\\d).*$" "5 apples, 6 pears and 0 grapes" =>
-              { matched = "5 apples, 6 pears and 0 grapes", index = 0, groups = [ "5", "6", "0" ] }
-            find "3" "01234" =>
-              { matched = "3", index = 3, groups = [ ] }
-          ```
-
-          Note that this function may perform better by sharing its partial application between multiple calls,
-          because in this case the underlying regular expression will only be compiled once (See the documentation
-          of `string.is_match` for more details).
-
-          **WARNING**: this function will currently return incorrect indices if `str`
-          contains Unicode extended grapheme clusters. The indices returned will
-          be the location of the matching codepoints, rather than the matching
-          grapheme clusters. This behaviour is expected to change in a future version.
-        "%
+        `find regex string` looks for matches of `regexp` in `string`. Returns
+        the part of `string` that matched, the index of the first character that
+        was part of the match in `string`, and an array of all capture groups if
+        there were any.
+
+        **WARNING**: this function will currently return incorrect indices if
+        `str` contains Unicode extended grapheme clusters. The indices returned
+        will be the location of the matching codepoints, rather than the
+        matching grapheme clusters.
+
+        # Examples
+
+        ```nickel
+        find "^(\\d).*(\\d).*(\\d).*$" "5 apples, 6 pears and 0 grapes" =>
+          { matched = "5 apples, 6 pears and 0 grapes", index = 0, groups = [ "5", "6", "0" ] }
+        find "3" "01234" =>
+          { matched = "3", index = 3, groups = [ ] }
+        ```
+
+        # Performance
+
+        Note that this function may perform better by sharing its partial application between multiple calls,
+        because in this case the underlying regular expression will only be compiled once (see the documentation
+        of `string.is_match` for more details).
+      "%
       = fun regex => %str_find% regex,
 
     length
       : String -> Number
       | doc m%"
-      Returns the length of the string, as measured by the number of UTF-8
-      [extended grapheme clusters](https://unicode.org/glossary/#extended_grapheme_cluster).
+        Returns the length of the string, as measured by the number of Unicode
+        [extended grapheme clusters](https://unicode.org/glossary/#extended_grapheme_cluster).
+
+        Generally speaking, this gives the number of "visible" glyphs in the string.
 
-      Generally speaking, this gives the number of "visible" glyphs in the string.
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         length "" =>
           0
         length "hi" =>
@@ -380,126 +410,140 @@
           4
         length "👨🏾‍❤️‍💋‍👨🏻" =>
           1
-      ```
+        ```
       "%
       = fun s => %str_length% s,
 
     substring
       : Number -> Number -> String -> String
       | doc m%"
-      Takes a slice from the string. Errors if either index is out of range.
+        `substring start end str` takes the slice of `str` from `start` (included) to `end` (excluded).
+
+        The index arguments are the indices of Unicode extended grapheme clusters
+        rather than codepoints.
 
-      The index arguments are the indices of Unicode extended grapheme clusters
-      rather than codepoints.
+        # Preconditions
 
-      For example:
-      ```nickel
+        In `substring start end str`, `start` and `end` must be positive integers
+        such that `0 <= start <= end <= array.length value`.
+
+        # Examples
+
+        ```nickel
         substring 3 5 "abcdef" =>
           "de"
         substring 3 10 "abcdef" =>
           error
         substring (-3) 4 "abcdef" =>
           error
-      ```
+        ```
       "%
       = fun start end s => %str_substr% s start end,
 
     from
       | Stringable -> String
       | doc m%"
-      Converts a correct value to a string representation. Same as
-      `builtin.to_string`.
-
-      For example:
-      ```nickel
-      from 42 =>
-        "42"
-      from `Foo =>
-        "Foo"
-      from null =>
-        "null"
+        Converts a stringable value to its string representation. Same as
+        `builtin.to_string`.
+
+        # Examples
+
+        ```nickel
+        from 42 =>
+          "42"
+        from `Foo =>
+          "Foo"
+        from null =>
+          "null"
+        ```
       "%
       = fun x => %to_str% x,
 
     from_number
       | Number -> String
       | doc m%"
-      Converts a number to its string representation.
+        Converts a number to its string representation.
 
-      For example:
-      ```nickel
-      from_number 42 =>
-        "42"
-      ```
+        # Examples
+
+        ```nickel
+        from_number 42 =>
+          "42"
+        ```
       "%
       = from,
 
     from_enum
       | enum.Tag -> String
       | doc m%"
-      Converts an enum variant to its string representation.
+        Converts an enum variant to its string representation.
 
-      For example:
-      ```nickel
-      from_enum `MyEnum =>
-        "MyEnum"
-      ```
+        # Examples
+
+        ```nickel
+        from_enum `MyEnum =>
+          "MyEnum"
+        ```
       "%
       = from,
 
     from_bool
       | Bool -> String
       | doc m%"
-      Converts a boolean value to its string representation.
+        Converts a boolean value to its string representation.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         from_bool true =>
           "true"
-      ```
+        ```
       "%
       = from,
 
     to_number
-      | NumLiteral -> Number
+      | NumberLiteral -> Number
       | doc m%"
-      Converts a string that represents an integer to that integer.
+        Converts a string that represents a number to that number.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         to_number "123" =>
           123
-      ```
+        ```
       "%
       = fun s => %num_from_str% s,
 
     to_bool
       | BoolLiteral -> Bool
       | doc m%"
-      Converts a string that represents a boolean to that boolean.
+        Converts a string that represents a bool to that bool.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         to_bool "true" =>
           true
         to_bool "True" =>
           true
         to_bool "false" =>
           false
-      ```
+        ```
       "%
       = fun s => s == "true",
 
     to_enum
       | String -> enum.Tag
       | doc m%"
-      Converts any string that represents an enum variant to that enum variant.
+        Converts any string that represents an enum variant to that enum variant.
+
+        # Examples
 
-      For example:
-      ```nickel
+        ```nickel
         to_enum "Hello" =>
           `Hello
-      ```
+        ```
       "%
       = fun s => %enum_from_str% s,
   }


Test patch:
diff --git a/tests/integration/pass/stdlib_string_contracts.ncl b/tests/integration/pass/stdlib_string_contracts.ncl
index e2d59244..e41ad935 100644
--- a/tests/integration/pass/stdlib_string_contracts.ncl
+++ b/tests/integration/pass/stdlib_string_contracts.ncl
@@ -7,10 +7,10 @@ let {check, ..} = import "lib/assert.ncl" in
   ("true" | string.BoolLiteral) == "true",
   ("false" | string.BoolLiteral) == "false",
 
-  # string.NumLiteral
-  ("+1.2" | string.NumLiteral) == "+1.2",
-  ("-1.2" | string.NumLiteral) == "-1.2",
-  ("9001" | string.NumLiteral) == "9001",
+  # string.NumberLiteral
+  ("+1.2" | string.NumberLiteral) == "+1.2",
+  ("-1.2" | string.NumberLiteral) == "-1.2",
+  ("9001" | string.NumberLiteral) == "9001",
 
   # string.Character
   # single ascii chars