Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add foldl and foldr aliases to Foldable #2020

Merged
merged 3 commits into from
Nov 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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