Repository: calmm-js/partial.lenses
Pull request #39 (2017-01-02 08:09:16)

WIP: More folds

codecov-io: ## [Current coverage](https://codecov.io/gh/calmm-js/partial.lenses/pull/39?src=pr) is 100% (diff: 100%)
> Merging [#39](https://codecov.io/gh/calmm-js/partial.lenses/pull/39?src=pr) into [master](https://codecov.io/gh/calmm-js/partial.lenses/branch/master?src=pr) will not change coverage

```diff
@@           master   #39   diff @@
===================================
  Files           1     1          
  Lines         275   294    +19   
  Methods         0     0          
  Messages        0     0          
  Branches        0     0          
===================================
+ Hits          275   294    +19   
  Misses          0     0          
  Partials        0     0          
```

> Powered by [Codecov](https://codecov.io?src=pr). Last update [c6531c6...a345f2d](https://codecov.io/gh/calmm-js/partial.lenses/compare/c6531c629528a4a7132a2554a583a8a9a0088af1...a345f2d31c18834ffc4b5e7d1a53e8c8bb22ca1e?src=pr)

Patch:
diff --git a/README.md b/README.md
index 8b2a8cc..f004901 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,11 @@ parts.  [Try Lenses!](https://calmm-js.github.io/partial.lenses/)
       * [`L.collect(traversal, maybeData)`](#L-collect "L.collect: PTraversal s a -> Maybe s -> [a]")
       * [`L.collectMap(traversal, (maybeValue, index) => maybeValue, maybeData)`](#L-collectMap "L.collectMap: PTraversal s a -> ((Maybe a, Index) -> Maybe b) -> Maybe s -> [b]")
       * [`L.foldMapOf({empty: () => value, concat: (value, value) => value}, traversal, (maybeValue, index) => value, maybeData)`](#L-foldMapOf "L.foldMapOf: {empty: () -> r, concat: (r, r) -> r} -> PTraversal s a -> ((Maybe a, Index) -> r) -> Maybe s -> r")
+      * [`L.foldOf({empty: () => value, concat: (value, value) => value}, traversal, maybeData)`](#L-foldOf "L.foldOf: {empty: () -> a, concat: (a, a) -> a} -> PTraversal s a -> Maybe s -> a")
+      * [`L.foldlOf(traversal, (value, maybeValue, index) => value, value, maybeData)`](#L-foldlOf "L.foldlOf: PTraversal s a -> ((r, Maybe a, Index) -> r) -> r -> Maybe s -> r")
+      * [`L.foldrOf(traversal, (value, maybeValue, index) => value, value, maybeData)`](#L-foldrOf "L.foldrOf: PTraversal s a -> ((r, Maybe a, Index) -> r) -> r -> Maybe s -> r")
+      * [`L.productOf(traversal, maybeData)`](#L-productOf "L.productOf: PTraversal s Number -> Maybe s -> Number")
+      * [`L.sumOf(traversal, maybeData)`](#L-sumOf "L.sumOf: PTraversal s Number -> Maybe s -> Number")
     * [Creating new traversals](#creating-new-traversals)
       * [`L.branch({prop: traversal, ...props})`](#L-branch "L.branch: {p1: PTraversal s a, ...pts} -> PTraversal s a")
     * [Traversals and combinators](#traversals-and-combinators)
@@ -829,6 +834,70 @@ L.foldMapOf(Sum, L.sequence, x => x, [1, 2, 3])
 // 6
 ```
 
+##### <a name="L-foldOf"></a> [≡](#contents) [`L.foldOf({empty: () => value, concat: (value, value) => value}, traversal, maybeData)`](#L-foldOf "L.foldOf: {empty: () -> a, concat: (a, a) -> a} -> PTraversal s a -> Maybe s -> a")
+
+`L.foldOf({empty, concat}, t, s)` performs a fold, using the given `concat` and
+`empty` operations, over the elements focused on by the given traversal or lens
+`t` from the given data structure `s`.  The `concat` operation and the constant
+returned by `empty()` should form
+a
+[monoid](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monoid) over
+the values focused on by `t`.
+
+For example:
+
+```js
+L.foldOf(Sum, L.sequence, [1, 2, 3])
+// 6
+```
+
+##### <a name="L-foldlOf"></a> [≡](#contents) [`L.foldlOf(traversal, (value, maybeValue, index) => value, value, maybeData)`](#L-foldlOf "L.foldlOf: PTraversal s a -> ((r, Maybe a, Index) -> r) -> r -> Maybe s -> r")
+
+`L.foldlOf` performs a fold from left over the elements focused on by the given
+traversal.
+
+For example:
+
+```js
+L.foldlOf(L.sequence, (x, y) => x + y, 0, [1,2,3])
+// 6
+```
+
+##### <a name="L-foldrOf"></a> [≡](#contents) [`L.foldrOf(traversal, (value, maybeValue, index) => value, value, maybeData)`](#L-foldrOf "L.foldrOf: PTraversal s a -> ((r, Maybe a, Index) -> r) -> r -> Maybe s -> r")
+
+`L.foldrOf` performs a fold from right over the elements focused on by the given
+traversal.
+
+For example:
+
+```js
+L.foldrOf(L.sequence, (x, y) => x * y, 1, [1,2,3])
+// 6
+```
+
+##### <a name="L-productOf"></a> [≡](#contents) [`L.productOf(traversal, maybeData)`](#L-productOf "L.productOf: PTraversal s Number -> Maybe s -> Number")
+
+`L.productOf` computes the product of the optional numbers targeted by the
+traversal.
+
+For example:
+
+```js
+L.productOf(L.sequence, [1,2,3])
+// 6
+```
+
+##### <a name="L-sumOf"></a> [≡](#contents) [`L.sumOf(traversal, maybeData)`](#L-sumOf "L.sumOf: PTraversal s Number -> Maybe s -> Number")
+
+`L.sumOf` computes the sum of the optional numbers targeted by the traversal.
+
+For example:
+
+```js
+L.sumOf(L.sequence, [1,2,3])
+// 6
+```
+
 #### Creating new traversals
 
 ##### <a name="L-branch"></a> [≡](#contents) [`L.branch({prop: traversal, ...props})`](#L-branch "L.branch: {p1: PTraversal s a, ...pts} -> PTraversal s a")
@@ -1833,10 +1902,10 @@ new part.  Setting an existing part to `undefined` removes it.
 
 ### Performance
 
-Here are a few benchmarks on partial lenses (as `L` version 7.0.0) and some
-roughly equivalent operations using [Ramda](http://ramdajs.com/) (as `R` version
-0.22.1) and [Ramda Lens](https://github.com/ramda/ramda-lens) (as `P` version
-0.1.1).
+Here are a few benchmark results on partial lenses (as `L` version 7.0.0) and
+some roughly equivalent operations using [Ramda](http://ramdajs.com/) (as `R`
+version 0.22.1) and [Ramda Lens](https://github.com/ramda/ramda-lens) (as `P`
+version 0.1.1).
 
 ```jsx
 L.foldMapOf(Sum, L.sequence, id, xs100) x  1,337,324 ops/sec ±0.45% (181 runs sampled)
diff --git a/bench/bench.js b/bench/bench.js
index d67d1f5..36fec72 100644
--- a/bench/bench.js
+++ b/bench/bench.js
@@ -41,6 +41,7 @@ const id = I.id
 const always = I.always
 const inc = x => x + 1
 const add = (x, y) => x+y
+const addC = x => y => x+y
 
 const Sum = {empty: () => 0, concat: add}
 const List = {empty: always([]), concat: (x, y) => x.concat(y)}
@@ -87,37 +88,74 @@ const values = L.lazy(rec => [
 const bstPairs = [[3, "g"], [2, "a"], [1, "m"], [4, "i"], [5, "c"]]
 const bst = fromPairs(bstPairs)
 
-const bs = [
+const Benchmark = require("benchmark")
+Benchmark.options.maxTime = 10
+
+R.forEach(bs => {
+  global.gc()
+  const s = new Benchmark.Suite()
+  bs.forEach(b => {
+    b = b.replace(/[ \n]+/g, " ")
+    s.add(b, eval("() => " + b))
+  })
+  s.on('complete', complete)
+  s.run()
+}, [
   [
+    `L.foldrOf(L.sequence, add, 0, xs100)`,
+    `O.Fold.foldrOf(O.Traversal.traversed, addC, 0, xs100)`,
+  ], [
+    `L.foldlOf(L.sequence, add, 0, xs100)`,
+    `O.Fold.foldlOf(O.Traversal.traversed, addC, 0, xs100)`,
+  ], [
     'L.foldMapOf(Sum, L.sequence, id, xs100)',
+    'L.sumOf(L.sequence, id, xs100)',
+    'O.Fold.sumOf(O.Traversal.traversed, xs100)',
     'P.sumOf(P.traversed, xs100)',
     'R.sum(xs100)',
-    'O.Fold.sumOf(O.Traversal.traversed, xs100)',
+  ], [
+    'L.foldMapOf(Sum, [L.sequence, L.sequence, L.sequence], id, xsss100)',
+    'L.sumOf([L.sequence, L.sequence, L.sequence], xsss100)',
+    `O.Fold.sumOf(R.compose(O.Traversal.traversed,
+                            O.Traversal.traversed,
+                            O.Traversal.traversed),
+                  xsss100)`,
+    'P.sumOf(R.compose(P.traversed, P.traversed, P.traversed), xsss100)',
   ], [
     'L.collect(L.sequence, xs100)',
     'O.Fold.toListOf(O.Traversal.traversed, xs100)',
+  ], [
+    'L.collect([L.sequence, L.sequence, L.sequence], xsss100)',
+    `O.Fold.toListOf(R.compose(O.Traversal.traversed,
+                               O.Traversal.traversed,
+                               O.Traversal.traversed),
+                     xsss100)`,
+    'R.chain(R.chain(R.identity), xsss100)',
+  ], [
+    'L.collect(flatten, xsss100)',
+    'R.flatten(xsss100)',
   ], [
     'L.modify(L.sequence, inc, xs100)',
+    'O.Setter.over(O.Traversal.traversed, inc, xs100)',
     'P.over(P.traversed, inc, xs100)',
     'R.map(inc, xs100)',
-    'O.Setter.over(O.Traversal.traversed, inc, xs100)',
   ], [
     'L.modify([L.sequence, L.sequence, L.sequence], inc, xsss100)',
-    'P.over(R.compose(P.traversed, P.traversed, P.traversed), inc, xsss100)',
-    'R.map(R.map(R.map(inc)), xsss100)',
     `O.Setter.over(R.compose(O.Traversal.traversed,
                              O.Traversal.traversed,
                              O.Traversal.traversed),
                    inc,
                    xsss100)`,
+    'P.over(R.compose(P.traversed, P.traversed, P.traversed), inc, xsss100)',
+    'R.map(R.map(R.map(inc)), xsss100)',
   ], [
     'L.get(1, xs)',
     'R.nth(1, xs)',
     'R.view(l_1, xs)',
   ], [
     'L.set(1, 0, xs)',
-    'R.update(1, 0, xs)',
     'R.set(l_1, 0, xs)',
+    'R.update(1, 0, xs)',
   ], [
     'L.get("y", xyz)',
     'R.prop("y", xyz)',
@@ -129,17 +167,17 @@ const bs = [
   ], [
     'L.get([0,"x",0,"y"], axay)',
     'R.path([0,"x",0,"y"], axay)',
-    'R.view(l_0x0y, axay)',
     'R.view(l_0_x_0_y, axay)',
+    'R.view(l_0x0y, axay)',
   ], [
     'L.set([0,"x",0,"y"], 0, axay)',
     'R.assocPath([0,"x",0,"y"], 0, axay)',
-    'R.set(l_0x0y, 0, axay)',
     'R.set(l_0_x_0_y, 0, axay)',
+    'R.set(l_0x0y, 0, axay)',
   ], [
     'L.modify([0,"x",0,"y"], inc, axay)',
-    'R.over(l_0x0y, inc, axay)',
     'R.over(l_0_x_0_y, inc, axay)',
+    'R.over(l_0x0y, inc, axay)',
   ], [
     'L.remove(1, xs)',
     'R.remove(1, 1, xs)',
@@ -148,16 +186,16 @@ const bs = [
     'R.dissoc("y", xyz)',
   ], [
     'L.get(["x","y","z"], xyzn)',
+    'O.Getter.view(o_x_y_z, xyzn)',
     'R.path(["x","y","z"], xyzn)',
-    'R.view(l_xyz, xyzn)',
     'R.view(l_x_y_z, xyzn)',
-    'O.Getter.view(o_x_y_z, xyzn)',
+    'R.view(l_xyz, xyzn)',
   ], [
     'L.set(["x","y","z"], 0, xyzn)',
+    'O.Setter.set(o_x_y_z, 0, xyzn)',
     'R.assocPath(["x","y","z"], 0, xyzn)',
-    'R.set(l_xyz, 0, xyzn)',
     'R.set(l_x_y_z, 0, xyzn)',
-    'O.Setter.set(o_x_y_z, 0, xyzn)',
+    'R.set(l_xyz, 0, xyzn)',
   ], [
     'L.remove(50, xs100)',
     'R.remove(50, 1, xs100)',
@@ -179,27 +217,22 @@ const bs = [
     'R.update(5000, 2, xs10000)',
   ], [
     'L.modify(values, x => x + x, bst)',
+  ], [
     'L.collect(values, bst)',
   ], [
     'fromPairs(bstPairs)',
   ], [
-    'L.get(L.defaults(1), undefined)',
     'L.get(L.defaults(1), 2)',
+    'L.get(L.defaults(1), undefined)',
   ], [
-    'L.get(L.define(1), undefined)',
     'L.get(L.define(1), 2)',
+    'L.get(L.define(1), undefined)',
   ], [
-    'L.get(L.valueOr(1), undefined)',
-    'L.get(L.valueOr(1), null)',
     'L.get(L.valueOr(1), 2)',
+    'L.get(L.valueOr(1), null)',
+    'L.get(L.valueOr(1), undefined)',
   ], [
     'L.foldMapOf(List, L.sequence, toList, xs100)',
-  ], [
-    'L.collect([L.sequence, L.sequence, L.sequence], xsss100)',
-    'R.chain(R.chain(R.identity), xsss100)',
-  ], [
-    'L.collect(flatten, xsss100)',
-    'R.flatten(xsss100)',
   ], [
     'L.modify(flatten, inc, xsss100)',
   ], [
@@ -210,14 +243,8 @@ const bs = [
     'L.set(L.findWith("x"), 2, axay)',
   ], [
     'L.set(L.props("x", "y"), {x:2, y:3}, {x:1, y:2, z:4})',
-  ], [
-    'L.foldMapOf(Sum, [L.sequence, L.sequence, L.sequence], id, xsss100)',
-    'P.sumOf(R.compose(P.traversed, P.traversed, P.traversed), xsss100)',
   ]
-]
-
-const Benchmark = require("benchmark")
-Benchmark.options.maxTime = 10
+])
 
 function complete() {
   const bs = I.seq(this,
@@ -229,20 +256,9 @@ function complete() {
                         R.map(R.prop("hz")),
                         R.reduce(R.max, 0))
   bs.forEach(b => {
-    console.log(sprintf('%12s/s  %6.2fx  %s',
+    console.log(sprintf('%12s/s %7.2fx  %s',
                         Math.round(b.hz).toLocaleString(),
                         fastest/b.hz, b.name))
   })
   console.log()
 }
-
-bs.forEach(bs => {
-  global.gc()
-  const s = new Benchmark.Suite()
-  bs.forEach(b => {
-    b = b.replace(/[ \n]+/g, " ")
-    s.add(b, eval("() => " + b))
-  })
-  s.on('complete', complete)
-  s.run()
-})
diff --git a/src/partial.lenses.js b/src/partial.lenses.js
index 68b1562..f4fd62b 100644
--- a/src/partial.lenses.js
+++ b/src/partial.lenses.js
@@ -19,6 +19,10 @@ import {
 
 //
 
+function pair(x0, x1) {return [x0, x1]}
+
+//
+
 function mapPartialIndexU(xi2y, xs) {
   const ys = [], n=xs.length
   for (let i=0, y; i<n; ++i)
@@ -49,23 +53,31 @@ const run = (o, C, xi2yC, s, i) => toFunction(o)(C, xi2yC, s, i)
 
 //
 
+function mkFoldDefined(empty, tacnoc) {
+  const a = ConstFlip(empty, tacnoc)
+  const f = x => isDefined(x) ? x : empty
+  return curry2((t, s) => run(t, a, f, s))
+}
+
+//
+
 const reqApplicative = f => assert(f, id, "Traversals require an applicative.")
 
 //
 
 function Concat(l, r) {this.l = l; this.r = r}
 
-const isConcat = n => n && n.constructor === Concat
+const isConcat = n => n.constructor === Concat
 
 const ap = (r, l) => isDefined(l) ? isDefined(r) ? new Concat(l, r) : l : r
 
 const rconcat = t => h => ap(t, h)
 
 function pushTo(n, ys) {
-  while (isConcat(n)) {
+  while (n && isConcat(n)) {
     const l = n.l
     n = n.r
-    if (isConcat(l)) {
+    if (l && isConcat(l)) {
       pushTo(l.l, ys)
       pushTo(l.r, ys)
     } else
@@ -82,6 +94,19 @@ function toArray(n) {
   }
 }
 
+function foldlRec(f, r, n) {
+  while (isConcat(n)) {
+    const l = n.l
+    n = n.r
+    r = isConcat(l)
+      ? foldlRec(f, foldlRec(f, r, l.l), l.r)
+      : f(r, l[0], l[1])
+  }
+  return f(r, n[0], n[1])
+}
+
+const foldl = (f, r, n) => isDefined(n) ? foldlRec(f, r, n) : r
+
 const Collect = ConstFlip(void 0, ap)
 
 const collectMapU = (t, xi2y, s) =>
@@ -387,9 +412,27 @@ export const collect = curry2((t, s) => collectMapU(t, id, s))
 
 export const collectMap = curry3(collectMapU)
 
+export const foldOf = curry3((m, t, s) => run(t, ConstOf(m), id, s))
+
 export const foldMapOf =
   curry4((m, t, xMi2y, s) => run(t, ConstOf(m), xMi2y, s))
 
+export const sumOf = mkFoldDefined(0, (y, x) => x + y)
+
+export const productOf = mkFoldDefined(1, (y, x) => x * y)
+
+export const foldrOf = curry4((t, f, r, s) => {
+  const xs = collectMapU(t, pair, s)
+  for (let i=xs.length-1; 0<=i; --i) {
+    const x = xs[i]
+    r = f(r, x[0], x[1])
+  }
+  return r
+})
+
+export const foldlOf = curry4((t, f, r, s) =>
+  foldl(f, r, run(t, Collect, pair, s)))
+
 // Creating new traversals
 
 export function branch(template) {


Test patch:
diff --git a/test/core.js b/test/core.js
index 6584707..12a7611 100644
--- a/test/core.js
+++ b/test/core.js
@@ -8,6 +8,12 @@ const isDefined = x => x !== undefined
 const Collect = {empty: R.always([]), concat: (x, y) => x.concat(y)}
 const toCollect = x => isDefined(x) ? [x] : []
 
+const foldxOf = concat => R.curry((t, f, r, s) =>
+  foldMapOf({empty: () => R.identity, concat},
+            t, (x, i) => r => f(r, x, i), s)(r))
+const foldDefinedOf = m => R.curry((t, s) =>
+  foldMapOf(m, [t, optional], R.identity, s))
+
 // Optics
 
 export const modify = L.modify
@@ -37,8 +43,16 @@ export const toFunction = L.toFunction
 export const collect = R.curry((t, s) => collectMap(t, R.identity, s))
 export const collectMap = R.curry((t, to, s) =>
   foldMapOf(Collect, t, R.pipe(to, toCollect), s))
+
+export const foldOf = R.curry((m, t, s) => foldMapOf(m, t, R.identity, s))
 export const foldMapOf = L.foldMapOf
 
+export const sumOf = foldDefinedOf({empty: () => 0, concat: R.add})
+export const productOf = foldDefinedOf({empty: () => 1, concat: R.multiply})
+
+export const foldrOf = foldxOf(R.compose)
+export const foldlOf = foldxOf(R.pipe)
+
 export const branch = L.branch
 
 export const optional = when(isDefined)
diff --git a/test/tests.js b/test/tests.js
index 319b4e3..075bc31 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -18,8 +18,11 @@ function show(x) {
   }
 }
 
+const a100000 = Array(100000).fill(1)
+
 const run = expr =>
-  eval(`(P, L, R, id, I, C, T) => ${expr}`)(P, L, R, id, I, C, T)
+  eval(`(P, L, X, R, id, I, C, T, a100000) => ${expr}`)(
+         P, L, L, R, id, I, C, T, a100000)
 
 function testEq(exprIn, expect) {
   const expr = exprIn.replace(/[ \n]+/g, " ")
@@ -415,6 +418,7 @@ describe("L.collect", () => {
   testEq(`L.collect(["a", L.sequence, "b", L.sequence, "c", L.sequence],
                     {a:[{b:[]},{b:[{c:[1]}]},{b:[]},{b:[{c:[2]}]}]})`,
          [1,2])
+  testEq('X.collect(X.sequence, a100000).length', 100000)
 })
 
 describe("L.collectMap", () => {
@@ -436,6 +440,24 @@ describe("L.foldMapOf", () => {
          6)
 })
 
+describe("folds", () => {
+  testEq(`X.foldOf(Sum, X.sequence, a100000)`, 100000)
+  testEq(`L.sumOf([L.sequence, "x"], undefined)`, 0)
+  testEq(`L.productOf([L.sequence, "x"], undefined)`, 1)
+  testEq(`L.sumOf([L.sequence, "x"], [{x:-2},{y:1},{x:-3}])`, -5)
+  testEq(`L.productOf([L.sequence, "x"], [{x:-2},{y:1},{x:-3}])`, 6)
+  testEq(`L.foldrOf([L.sequence, L.sequence], (x,y) => [x,y], 0, [])`, 0)
+  testEq(`L.foldlOf([L.sequence, L.sequence], (x,y) => [x,y], 0, [])`, 0)
+  testEq(`L.foldrOf([L.sequence, L.sequence], (x,y) => [x,y], 0, [[1,2],[3]])`,
+         [[[0,3],2],1])
+  testEq(`L.foldlOf([L.sequence, L.sequence], (x,y) => [x,y], 0, [[1,2],[3]])`,
+         [[[0,1],2],3])
+  ;['foldlOf', 'foldrOf'].forEach(fold => {
+    testEq(`X.${fold}(X.sequence, (x,y) => x+y, 0, a100000)`,
+           100000)
+  })
+})
+
 describe("L.pick", () => {
   testEq('L.get(L.pick({x: "c"}), {a: [2], b: 1})', undefined)
   testEq('L.set([L.pick({x: "c"}), "x"], 4, {a: [2], b: 1})',
diff --git a/test/type.js b/test/type.js
index 6a9147c..c7e68ef 100644
--- a/test/type.js
+++ b/test/type.js
@@ -38,6 +38,7 @@ const instance = c => fromPredicate(x => x instanceof c)
 export const integer = fromPredicate(Number.isInteger)
 export const nonNegative = and(integer, fromPredicate(x => 0 <= x))
 
+export const number = type("number")
 export const string = type("string")
 export const undef = fromPredicate(x => x === undefined)
 export const def = fromPredicate(x => x !== undefined)
diff --git a/test/types.js b/test/types.js
index a3e0052..f3e20f6 100644
--- a/test/types.js
+++ b/test/types.js
@@ -33,6 +33,9 @@ const T_traversal = T_opticOf(T_applicative)
 const T_lens = T_optic
 const T_isomorphism = T_lens
 
+const T_monoid = T.object({empty: T.fn([], T.any),
+                           concat: T.fn([T.any, T.any], T.any)})
+
 //
 
 export const modify = T.fn([T_optic,
@@ -63,13 +66,24 @@ export const collectMap = T.fn([T_traversal,
                                 T.fn([T_maybeData, T_index], T_maybeData),
                                 T_maybeData],
                                T.array(T_data))
-export const foldMapOf = T.fn([T.object({empty: T.fn([], T.any),
-                                         concat: T.fn([T.any, T.any], T.any)}),
+export const foldOf = T.fn([T_monoid, T_traversal, T_maybeData], T.any)
+export const foldMapOf = T.fn([T_monoid,
                                T_traversal,
                                T.fn([T_maybeData, T_index], T.any),
                                T_maybeData],
                               T.any)
 
+export const sumOf = T.fn([T_traversal, T_maybeData], T.number)
+export const productOf = sumOf
+
+export const foldrOf =
+  T.fn([T_traversal,
+        T.fn([T.any, T_maybeData, T_index], T.any),
+        T.any,
+        T_maybeData],
+       T.any)
+export const foldlOf = foldrOf
+
 export const branch = T.fn([T.props(T_traversal)], T_traversal)
 
 export const sequence = T_traversal