From 138bb6f9399e787494aeb014850162dd3e9be365 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Tue, 7 Nov 2017 18:36:47 +0100 Subject: [PATCH 1/3] Add `foldl` and `foldr` aliases to `Foldable` --- core/src/main/scala/cats/syntax/foldable.scala | 11 +++++++++++ tests/src/test/scala/cats/tests/SyntaxSuite.scala | 2 ++ 2 files changed, 13 insertions(+) diff --git a/core/src/main/scala/cats/syntax/foldable.scala b/core/src/main/scala/cats/syntax/foldable.scala index 87902c3ca3..eb6bb70460 100644 --- a/core/src/main/scala/cats/syntax/foldable.scala +++ b/core/src/main/scala/cats/syntax/foldable.scala @@ -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 { @@ -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) +} diff --git a/tests/src/test/scala/cats/tests/SyntaxSuite.scala b/tests/src/test/scala/cats/tests/SyntaxSuite.scala index 2a28009324..736965a2fc 100644 --- a/tests/src/test/scala/cats/tests/SyntaxSuite.scala +++ b/tests/src/test/scala/cats/tests/SyntaxSuite.scala @@ -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] From 73469c5d69765a0e2874d87e86c64622eadd9d9c Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 8 Nov 2017 09:05:39 +0100 Subject: [PATCH 2/3] Add mima exception for `catsSyntaxFoldOps` --- build.sbt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 580d01213d..9ed50b92be 100644 --- a/build.sbt +++ b/build.sbt @@ -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) From 07a1dbb50e369f0a343a02ddae49c6602fbe86b1 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 9 Nov 2017 10:33:11 +0100 Subject: [PATCH 3/3] Add doctests for `foldr` and `foldl` in `Foldable` --- core/src/main/scala/cats/Foldable.scala | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index f8474ebdb7..6060804531 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -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 @@ -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]