Skip to content

Commit

Permalink
Merge pull request #925 from TomasMikula/foldM
Browse files Browse the repository at this point in the history
Stack-safe foldM
  • Loading branch information
stew committed Mar 16, 2016
2 parents 3f48848 + 5146e59 commit aa348c0
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 0 deletions.
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ import simulacrum.typeclass
def foldMap[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): B =
foldLeft(fa, B.empty)((b, a) => B.combine(b, f(a)))

/**
* Left associative monadic folding on `F`.
*/
def foldM[G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] =
foldLeft(fa, G.pure(z))((gb, a) => G.flatMap(gb)(f(_, a)))

/**
* Traverse `F[A]` using `Applicative[G]`.
*
Expand Down
16 changes: 16 additions & 0 deletions tests/src/test/scala/cats/tests/FoldableTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ class FoldableTestsAdditional extends CatsSuite {
// more basic checks
val names = List("Aaron", "Betty", "Calvin", "Deirdra")
F.foldMap(names)(_.length) should === (names.map(_.length).sum)
val sumM = F.foldM(names, "") { (acc, x) => (Some(acc + x): Option[String]) }
assert(sumM == Some("AaronBettyCalvinDeirdra"))
val notCalvin = F.foldM(names, "") { (acc, x) =>
if (x == "Calvin") (None: Option[String])
else (Some(acc + x): Option[String]) }
assert(notCalvin == None)

// test trampolining
val large = (1 to 10000).toList
Expand All @@ -73,6 +79,16 @@ class FoldableTestsAdditional extends CatsSuite {
larger.value should === (large.map(_ + 1))
}

test("Foldable[List].foldM stack safety") {
def nonzero(acc: Long, x: Long): Option[Long] =
if (x == 0) None else Some(acc + x)

val n = 100000L
val expected = n*(n+1)/2
val actual = Foldable[List].foldM((1L to n).toList, 0L)(nonzero)
assert(actual.get == expected)
}

test("Foldable[Stream]") {
val F = Foldable[Stream]

Expand Down

0 comments on commit aa348c0

Please sign in to comment.