Skip to content

Commit

Permalink
Add foldl and foldr aliases to Foldable (#2020)
Browse files Browse the repository at this point in the history
* Add `foldl` and `foldr` aliases to `Foldable`

* Add mima exception for `catsSyntaxFoldOps`

* Add doctests for `foldr` and `foldl` in `Foldable`
  • Loading branch information
felixmulder authored and kailuowang committed Nov 9, 2017
1 parent 6652845 commit 6863f3f
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 1 deletion.
9 changes: 8 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,14 @@ lazy val docSettings = Seq(
lazy val binaryCompatibleVersion = "1.0.0-RC1"

def mimaSettings(moduleName: String) = Seq(
mimaPreviousArtifacts := Set("org.typelevel" %% moduleName % binaryCompatibleVersion))
mimaPreviousArtifacts := Set("org.typelevel" %% moduleName % binaryCompatibleVersion),
// TODO: remove this post-release of 1.0.0
mimaBinaryIssueFilters += {
import com.typesafe.tools.mima.core._
import com.typesafe.tools.mima.core.ProblemFilters._
exclude[ReversedMissingMethodProblem]("cats.syntax.FoldableSyntax.catsSyntaxFoldOps")
}
)

lazy val docs = project
.enablePlugins(MicrositesPlugin)
Expand Down
47 changes: 47 additions & 0 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,27 @@ import simulacrum.typeclass

/**
* Left associative fold on 'F' using the function 'f'.
*
* Example:
* {{{
* scala> import cats.Foldable, cats.implicits._
* scala> val fa = Option(1)
*
* Folding by addition to zero:
* scala> Foldable[Option].foldLeft(fa, Option(0))((a, n) => a.map(_ + n))
* res0: Option[Int] = Some(1)
* }}}
*
* With syntax extensions, `foldLeft` can be used like:
* {{{
* Folding `Option` with addition from zero:
* scala> fa.foldLeft(Option(0))((a, n) => a.map(_ + n))
* res1: Option[Int] = Some(1)
*
* There's also an alias `foldl` which is equivalent:
* scala> fa.foldl(Option(0))((a, n) => a.map(_ + n))
* res2: Option[Int] = Some(1)
* }}}
*/
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B

Expand All @@ -43,6 +64,32 @@ import simulacrum.typeclass
*
* For more detailed information about how this method works see the
* documentation for `Eval[_]`.
*
* Example:
* {{{
* scala> import cats.Foldable, cats.Eval, cats.implicits._
* scala> val fa = Option(1)
*
* Folding by addition to zero:
* scala> val folded1 = Foldable[Option].foldRight(fa, Eval.now(0))((n, a) => a.map(_ + n))
* Since `foldRight` yields a lazy computation, we need to force it to inspect the result:
* scala> folded1.value
* res0: Int = 1
*
* With syntax extensions, we can write the same thing like this:
* scala> val folded2 = fa.foldRight(Eval.now(0))((n, a) => a.map(_ + n))
* scala> folded2.value
* res1: Int = 1
*
* Unfortunately, since `foldRight` is defined on many collections - this
* extension clashes with the operation defined in `Foldable`.
*
* To get past this and make sure you're getting the lazy `foldRight` defined
* in `Foldable`, there's an alias `foldr`:
* scala> val folded3 = fa.foldr(Eval.now(0))((n, a) => a.map(_ + n))
* scala> folded3.value
* res1: Int = 1
* }}}
*/
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B]

Expand Down
11 changes: 11 additions & 0 deletions core/src/main/scala/cats/syntax/foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ package syntax
trait FoldableSyntax extends Foldable.ToFoldableOps {
implicit final def catsSyntaxNestedFoldable[F[_]: Foldable, G[_], A](fga: F[G[A]]): NestedFoldableOps[F, G, A] =
new NestedFoldableOps[F, G, A](fga)

implicit final def catsSyntaxFoldOps[F[_]: Foldable, A](fa: F[A]): FoldableOps[F, A] =
new FoldableOps[F, A](fa)
}

final class NestedFoldableOps[F[_], G[_], A](val fga: F[G[A]]) extends AnyVal {
Expand All @@ -23,3 +26,11 @@ final class NestedFoldableOps[F[_], G[_], A](val fga: F[G[A]]) extends AnyVal {
*/
def foldK(implicit F: Foldable[F], G: MonoidK[G]): G[A] = F.foldK(fga)
}

final class FoldableOps[F[_], A](val fa: F[A]) extends AnyVal {
def foldl[B](b: B)(f: (B, A) => B)(implicit F: Foldable[F]): B =
F.foldLeft(fa, b)(f)

def foldr[B](b: Eval[B])(f: (A, Eval[B]) => Eval[B])(implicit F: Foldable[F]): Eval[B] =
F.foldRight(fa, b)(f)
}
2 changes: 2 additions & 0 deletions tests/src/test/scala/cats/tests/SyntaxSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ object SyntaxSuite extends AllInstances with AllSyntax {
val b = mock[B]
val f1 = mock[(B, A) => B]
val b0: B = fa.foldLeft(b)(f1)
val b1: B = fa.foldl(b)(f1)
val a0: A = fa.fold

val f2 = mock[(A, Eval[B]) => Eval[B]]
val lb0: Eval[B] = fa.foldRight(Now(b))(f2)
val lb1: Eval[B] = fa.foldr(Now(b))(f2)

val fz = mock[F[Z]]
val f3 = mock[Z => A]
Expand Down

0 comments on commit 6863f3f

Please sign in to comment.