Skip to content

Commit

Permalink
Merge pull request #1 from ceedubs/short-circuiting-foldLeftM
Browse files Browse the repository at this point in the history
Add a short-circuiting implementation of foldLeftM
  • Loading branch information
non committed Jun 3, 2016
2 parents b7afcb1 + 3eca17f commit f7567d8
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 4 deletions.
11 changes: 7 additions & 4 deletions free/src/main/scala/cats/free/Free.scala
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,14 @@ object Free {
* Perform a stack-safe monadic fold from the source context `F`
* into the target monad `G`.
*
* This method can express short-circuiting semantics, but like
* other left folds will traverse the entire `F[A]` structure. This
* means it is not suitable for potentially infinite structures.
* This method can express short-circuiting semantics. Even when
* `fa` is an infinite structure, this method can potentially
* terminate if the `foldRight` implementation for `F` and the
* `tailRecM` implementation for `G` are sufficiently lazy.
*/
def foldLeftM[F[_], G[_]: MonadRec, A, B](fa: F[A], z: B)(f: (B, A) => G[B])(implicit F: Foldable[F]): G[B] =
F.foldM[Free[G, ?], A, B](fa, z) { (b, a) => Free.liftF(f(b, a)) }.runTailRec
unsafeFoldLeftM[F, Free[G, ?], A, B](fa, z) { (b, a) => Free.liftF(f(b, a)) }.runTailRec

private def unsafeFoldLeftM[F[_], G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B])(implicit F: Foldable[F], G: Monad[G]): G[B] =
F.foldRight[A, B => G[B]](fa, Always(G.pure(_)))((a, lb) => Always((w: B) => G.flatMap(f(w, a))(lb.value))).value(z)
}
8 changes: 8 additions & 0 deletions free/src/test/scala/cats/free/FreeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ class FreeTests extends CatsSuite {
}
assert(res == Xor.left(3))
}

test(".foldLeftM short-circuiting") {
val ns = Stream.continually(1)
val res = Free.foldLeftM[Stream, Xor[Int, ?], Int, Int](ns, 0) { (sum, n) =>
if (sum >= 100000) Xor.left(sum) else Xor.right(sum + n)
}
assert(res == Xor.left(100000))
}
}

object FreeTests extends FreeTestsInstances {
Expand Down

0 comments on commit f7567d8

Please sign in to comment.