summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorzimbatm <zimbatm@zimbatm.com>2017-03-20 14:42:58 +0000
committerGitHub <noreply@github.com>2017-03-20 14:42:58 +0000
commit3bbab1757510de86d8062684f8c92d231265b934 (patch)
tree24d33d319eaba94c610e30f7082e7a5b5a5fd6bd /lib
parented59de18b5e98f6e71a5fd8efa2ff5104353cd8b (diff)
parent4f1d977877b5477d84ca1204ec7a3f87d6bbf871 (diff)
downloadnixlib-3bbab1757510de86d8062684f8c92d231265b934.tar
nixlib-3bbab1757510de86d8062684f8c92d231265b934.tar.gz
nixlib-3bbab1757510de86d8062684f8c92d231265b934.tar.bz2
nixlib-3bbab1757510de86d8062684f8c92d231265b934.tar.lz
nixlib-3bbab1757510de86d8062684f8c92d231265b934.tar.xz
nixlib-3bbab1757510de86d8062684f8c92d231265b934.tar.zst
nixlib-3bbab1757510de86d8062684f8c92d231265b934.zip
Merge pull request #23929 from Profpatsch/lib-lists-doc
Improve lib/trivial and lib/lists docs
Diffstat (limited to 'lib')
-rw-r--r--lib/lists.nix41
-rw-r--r--lib/tests.nix35
-rw-r--r--lib/trivial.nix33
3 files changed, 90 insertions, 19 deletions
diff --git a/lib/lists.nix b/lib/lists.nix
index 5e224921de81..82d5ba0124cb 100644
--- a/lib/lists.nix
+++ b/lib/lists.nix
@@ -16,17 +16,22 @@ rec {
   */
   singleton = x: [x];
 
-  /* "Fold" a binary function `op' between successive elements of
-     `list' with `nul' as the starting value, i.e., `fold op nul [x_1
-     x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'.  (This is
-     Haskell's foldr).
+  /* “right fold” a binary function `op' between successive elements of
+     `list' with `nul' as the starting value, i.e.,
+     `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'.
+     Type:
+       foldr :: (a -> b -> b) -> b -> [a] -> b
 
      Example:
-       concat = fold (a: b: a + b) "z"
+       concat = foldr (a: b: a + b) "z"
        concat [ "a" "b" "c" ]
        => "abcz"
+       # different types
+       strange = foldr (int: str: toString (int + 1) + str) "a"
+       strange [ 1 2 3 4 ]
+       => "2345a"
   */
-  fold = op: nul: list:
+  foldr = op: nul: list:
     let
       len = length list;
       fold' = n:
@@ -35,13 +40,25 @@ rec {
         else op (elemAt list n) (fold' (n + 1));
     in fold' 0;
 
-  /* Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
-     x_1) x_2) ... x_n)'.
+  /* `fold' is an alias of `foldr' for historic reasons */
+  # FIXME(Profpatsch): deprecate?
+  fold = foldr;
+
+
+  /* “left fold”, like `foldr', but from the left:
+     `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`.
+
+     Type:
+       foldl :: (b -> a -> b) -> b -> [a] -> b
 
      Example:
        lconcat = foldl (a: b: a + b) "z"
        lconcat [ "a" "b" "c" ]
        => "zabc"
+       # different types
+       lstrange = foldl (str: int: str + toString (int + 1)) ""
+       strange [ 1 2 3 4 ]
+       => "a2345"
   */
   foldl = op: nul: list:
     let
@@ -52,7 +69,7 @@ rec {
         else op (foldl' (n - 1)) (elemAt list n);
     in foldl' (length list - 1);
 
-  /* Strict version of foldl.
+  /* Strict version of `foldl'.
 
      The difference is that evaluation is forced upon access. Usually used
      with small whole results (in contract with lazily-generated list or large
@@ -140,7 +157,7 @@ rec {
        any isString [ 1 { } ]
        => false
   */
-  any = builtins.any or (pred: fold (x: y: if pred x then true else y) false);
+  any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false);
 
   /* Return true iff function `pred' returns true for all elements of
      `list'.
@@ -151,7 +168,7 @@ rec {
        all (x: x < 3) [ 1 2 3 ]
        => false
   */
-  all = builtins.all or (pred: fold (x: y: if pred x then y else false) true);
+  all = builtins.all or (pred: foldr (x: y: if pred x then y else false) true);
 
   /* Count how many times function `pred' returns true for the elements
      of `list'.
@@ -219,7 +236,7 @@ rec {
        => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
   */
   partition = builtins.partition or (pred:
-    fold (h: t:
+    foldr (h: t:
       if pred h
       then { right = [h] ++ t.right; wrong = t.wrong; }
       else { right = t.right; wrong = [h] ++ t.wrong; }
diff --git a/lib/tests.nix b/lib/tests.nix
index bef9cdee696d..1a9a5accd1c0 100644
--- a/lib/tests.nix
+++ b/lib/tests.nix
@@ -1,3 +1,6 @@
+# to run these tests:
+# nix-instantiate --eval --strict nixpkgs/lib/tests.nix
+# if the resulting list is empty, all tests passed
 let inherit (builtins) add; in
 with import ./default.nix;
 
@@ -45,10 +48,34 @@ runTests {
     expected = ["b" "c"];
   };
 
-  testFold = {
-    expr = fold (builtins.add) 0 (range 0 100);
-    expected = 5050;
-  };
+  testFold =
+    let
+      f = op: fold: fold op 0 (range 0 100);
+      # fold with associative operator
+      assoc = f builtins.add;
+      # fold with non-associative operator
+      nonAssoc = f builtins.sub;
+    in {
+      expr = {
+        assocRight = assoc foldr;
+        # right fold with assoc operator is same as left fold
+        assocRightIsLeft = assoc foldr == assoc foldl;
+        nonAssocRight = nonAssoc foldr;
+        nonAssocLeft = nonAssoc foldl;
+        # with non-assoc operator the fold results are not the same
+        nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr;
+        # fold is an alias for foldr
+        foldIsRight = nonAssoc fold == nonAssoc foldr;
+      };
+      expected = {
+        assocRight = 5050;
+        assocRightIsLeft = true;
+        nonAssocRight = 50;
+        nonAssocLeft = (-5050);
+        nonAssocRightIsNotLeft = true;
+        foldIsRight = true;
+      };
+    };
 
   testTake = testAllTrue [
     ([] == (take 0 [  1 2 3 ]))
diff --git a/lib/trivial.nix b/lib/trivial.nix
index abe5a16c67d6..40499b2b5092 100644
--- a/lib/trivial.nix
+++ b/lib/trivial.nix
@@ -1,17 +1,44 @@
 rec {
 
-  # Identity function.
+  /* The identity function
+     For when you need a function that does “nothing”.
+
+     Type: id :: a -> a
+  */
   id = x: x;
 
-  # Constant function.
+  /* The constant function
+     Ignores the second argument.
+     Or: Construct a function that always returns a static value.
+
+     Type: const :: a -> b -> a
+     Example:
+       let f = const 5; in f 10
+       => 5
+  */
   const = x: y: x;
 
-  # Named versions corresponding to some builtin operators.
+
+  ## Named versions corresponding to some builtin operators.
+
+  /* Concat two strings */
   concat = x: y: x ++ y;
+
+  /* boolean “or” */
   or = x: y: x || y;
+
+  /* boolean “and” */
   and = x: y: x && y;
+
+  /* Merge two attribute sets shallowly, right side trumps left
+
+     Example:
+       mergeAttrs { a = 1; b = 2; } // { b = 3; c = 4; }
+       => { a = 1; b = 3; c = 4; }
+  */
   mergeAttrs = x: y: x // y;
 
+
   # Compute the fixed point of the given function `f`, which is usually an
   # attribute set that expects its final, non-recursive representation as an
   # argument: