Commit 15a8d05b authored by hsjobeki's avatar hsjobeki
Browse files

init: lib.foldlAttrs

- provide comprehensive example
- add unit test
parent 624432c2
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -333,6 +333,66 @@ rec {
      ) (attrNames set)
    );

   /*
    Like builtins.foldl' but for attribute sets.
    Iterates over every name-value pair in the given attribute set.
    The result of the callback function is often called `acc` for accumulator. It is passed between callbacks from left to right and the final `acc` is the return value of `foldlAttrs`.

    Attention:
      There is a completely different function
      `lib.foldAttrs`
      which has nothing to do with this function, despite the similar name.

    Example:
      foldlAttrs
        (acc: name: value: {
          sum = acc.sum + value;
          names = acc.names ++ [name];
        })
        { sum = 0; names = []; }
        {
          foo = 1;
          bar = 10;
        }
      ->
        {
          sum = 11;
          names = ["bar" "foo"];
        }

      foldlAttrs
        (throw "function not needed")
        123
        {};
      ->
        123

      foldlAttrs
        (_: _: v: v)
        (throw "initial accumulator not needed")
        { z = 3; a = 2; };
      ->
        3

      The accumulator doesn't have to be an attrset.
      It can be as simple as a number or string.

      foldlAttrs
        (acc: _: v: acc * 10 + v)
        1
        { z = 1; a = 2; };
      ->
        121

    Type:
      foldlAttrs :: ( a -> String -> b -> a ) -> a -> { ... :: b } -> a
  */
  foldlAttrs = f: init: set:
    foldl'
      (acc: name: f acc name set.${name})
      init
      (attrNames set);

  /* Apply fold functions to values grouped by key.

     Example:
+1 −1
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ let
      composeManyExtensions makeExtensible makeExtensibleWithCustomName;
    inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
      getAttrFromPath attrVals attrValues getAttrs catAttrs filterAttrs
      filterAttrsRecursive foldAttrs collect nameValuePair mapAttrs
      filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs
      mapAttrs' mapAttrsToList concatMapAttrs mapAttrsRecursive mapAttrsRecursiveCond
      genAttrs isDerivation toDerivation optionalAttrs
      zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
+31 −0
Original line number Diff line number Diff line
@@ -533,6 +533,37 @@ runTests {
    };
  };

  # code from example
  testFoldlAttrs = {
    expr = {
      example = foldlAttrs
        (acc: name: value: {
          sum = acc.sum + value;
          names = acc.names ++ [ name ];
        })
        { sum = 0; names = [ ]; }
        {
          foo = 1;
          bar = 10;
        };
      # should just return the initial value
      emptySet = foldlAttrs (throw "function not needed") 123 { };
      # should just evaluate to the last value
      accNotNeeded = foldlAttrs (_acc: _name: v: v) (throw "accumulator not needed") { z = 3; a = 2; };
      # the accumulator doesnt have to be an attrset it can be as trivial as being just a number or string
      trivialAcc = foldlAttrs (acc: _name: v: acc * 10 + v) 1 { z = 1; a = 2; };
    };
    expected = {
      example = {
        sum = 11;
        names = [ "bar" "foo" ];
      };
      emptySet = 123;
      accNotNeeded = 3;
      trivialAcc = 121;
    };
  };

  # code from the example
  testRecursiveUpdateUntil = {
    expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {