Skip to content

Commit

Permalink
Document and optimize Foldable.iteratorFoldM
Browse files Browse the repository at this point in the history
  • Loading branch information
peterneyens committed Oct 21, 2016
1 parent 52c3b7f commit 381aa3d
Showing 1 changed file with 30 additions and 3 deletions.
33 changes: 30 additions & 3 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,38 @@ object Foldable {
loop()
}

/**
* Implementation of [[foldM]] which can short-circuit for
* structures with an `Iterator`.
*
* For example we can sum a `Stream` of integers and stop if
* the sum reaches 100 (if we reach the end of the `Stream`
* before getting to 100 we return the total sum) :
*
* {{{
* scala> import cats.implicits._
* scala> type LongOr[A] = Either[Long, A]
* scala> def sumStream(s: Stream[Int]): Long =
* | Foldable.iteratorFoldM[LongOr, Int, Long](s.toIterator, 0L){ (acc, n) =>
* | val sum = acc + n
* | if (sum < 100L) Right(sum) else Left(sum)
* | }.merge
*
* scala> sumStream(Stream.continually(1))
* res0: Long = 100
*
* scala> sumStream(Stream(1,2,3,4))
* res1: Long = 10
* }}}
*
* Note that `Foldable[Stream].foldM` uses this method underneath, so
* you wouldn't call this method explicitly like in the example above.
*/
def iteratorFoldM[M[_], A, B](it: Iterator[A], z: B)(f: (B, A) => M[B])(implicit M: Monad[M]): M[B] = {
val go: ((B, Iterator[A])) => M[Either[(B, Iterator[A]), B]] = { case (b, it) =>
if (it.hasNext) M.map(f(b, it.next))(b1 => Left((b1, it)))
val go: B => M[Either[B, B]] = { b =>
if (it.hasNext) M.map(f(b, it.next))(Left(_))
else M.pure(Right(b))
}
M.tailRecM((z, it))(go)
M.tailRecM(z)(go)
}
}

0 comments on commit 381aa3d

Please sign in to comment.