diff --git a/core/src/main/scala/cats/ApplicativeError.scala b/core/src/main/scala/cats/ApplicativeError.scala index ec4ab1bcda..7acd0a0eb5 100644 --- a/core/src/main/scala/cats/ApplicativeError.scala +++ b/core/src/main/scala/cats/ApplicativeError.scala @@ -1,6 +1,7 @@ package cats -import cats.data.EitherT +import cats.data.{EitherT, Validated} +import cats.data.Validated.{Invalid, Valid} import scala.reflect.ClassTag import scala.util.{Failure, Success, Try} @@ -186,6 +187,48 @@ trait ApplicativeError[F[_], E] extends Applicative[F] { case Left(e) => raiseError(e) } + /** + * Convert from scala.Option + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> import cats.ApplicativeError + * scala> val F = ApplicativeError[Either[String, *], String] + * + * scala> F.fromOption(Some(1), "Empty") + * res0: scala.Either[String, Int] = Right(1) + * + * scala> F.fromOption(Option.empty[Int], "Empty") + * res1: scala.Either[String, Int] = Left(Empty) + * }}} + */ + def fromOption[A](oa: Option[A], ifEmpty: => E): F[A] = + oa match { + case Some(a) => pure(a) + case None => raiseError(ifEmpty) + } + + /** + * Convert from cats.data.Validated + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> import cats.ApplicativeError + * + * scala> ApplicativeError[Option, Unit].fromValidated(1.valid[Unit]) + * res0: scala.Option[Int] = Some(1) + * + * scala> ApplicativeError[Option, Unit].fromValidated(().invalid[Int]) + * res1: scala.Option[Int] = None + * }}} + */ + def fromValidated[A](x: Validated[E, A]): F[A] = + x match { + case Invalid(e) => raiseError(e) + case Valid(a) => pure(a) + } } object ApplicativeError { diff --git a/core/src/main/scala/cats/Apply.scala b/core/src/main/scala/cats/Apply.scala index 3a43f93a43..fe9308b8c2 100644 --- a/core/src/main/scala/cats/Apply.scala +++ b/core/src/main/scala/cats/Apply.scala @@ -1,7 +1,6 @@ package cats -import simulacrum.typeclass -import simulacrum.noop +import simulacrum.{noop, typeclass} import cats.data.Ior /** @@ -214,6 +213,39 @@ trait Apply[F[_]] extends Functor[F] with InvariantSemigroupal[F] with ApplyArit val G = Apply[G] } + /** + * An `if-then-else` lifted into the `F` context. + * This function combines the effects of the `fcond` condition and of the two branches, + * in the order in which they are given. + * + * The value of the result is, depending on the value of the condition, + * the value of the first argument, or the value of the second argument. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * + * scala> val b1: Option[Boolean] = Some(true) + * scala> val asInt1: Option[Int] = Apply[Option].ifA(b1)(Some(1), Some(0)) + * scala> asInt1.get + * res0: Int = 1 + * + * scala> val b2: Option[Boolean] = Some(false) + * scala> val asInt2: Option[Int] = Apply[Option].ifA(b2)(Some(1), Some(0)) + * scala> asInt2.get + * res1: Int = 0 + * + * scala> val b3: Option[Boolean] = Some(true) + * scala> val asInt3: Option[Int] = Apply[Option].ifA(b3)(Some(1), None) + * asInt2: Option[Int] = None + * + * }}} + */ + @noop + def ifA[A](fcond: F[Boolean])(ifTrue: F[A], ifFalse: F[A]): F[A] = { + def ite(b: Boolean)(ifTrue: A, ifFalse: A) = if (b) ifTrue else ifFalse + ap2(map(fcond)(ite))(ifTrue, ifFalse) + } } object Apply { diff --git a/core/src/main/scala/cats/Bitraverse.scala b/core/src/main/scala/cats/Bitraverse.scala index dd521cf236..a0439e4137 100644 --- a/core/src/main/scala/cats/Bitraverse.scala +++ b/core/src/main/scala/cats/Bitraverse.scala @@ -1,6 +1,6 @@ package cats -import simulacrum.typeclass +import simulacrum.{noop, typeclass} /** * A type class abstracting over types that give rise to two independent [[cats.Traverse]]s. @@ -61,6 +61,52 @@ import simulacrum.typeclass override def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D] = bitraverse[Id, A, B, C, D](fab)(f, g) + + /** + * Traverse over the left side of the structure. + * For the right side, use the standard `traverse` from [[cats.Traverse]]. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * + * scala> val intAndString: (Int, String) = (7, "test") + * + * scala> Bitraverse[Tuple2].leftTraverse(intAndString)(i => Option(i).filter(_ > 5)) + * res1: Option[(Int, String)] = Some((7,test)) + * + * scala> Bitraverse[Tuple2].leftTraverse(intAndString)(i => Option(i).filter(_ < 5)) + * res2: Option[(Int, String)] = None + * }}} + */ + @noop + def leftTraverse[G[_], A, B, C](fab: F[A, B])(f: A => G[C])(implicit G: Applicative[G]): G[F[C, B]] = + bitraverse(fab)(f, G.pure(_)) + + /** + * Sequence the left side of the structure. + * For the right side, use the standard `sequence` from [[cats.Traverse]]. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * + * scala> val optionalErrorRight: Either[Option[String], Int] = Either.right(123) + * scala> optionalErrorRight.leftSequence + * res1: Option[Either[String, Int]] = Some(Right(123)) + * + * scala> val optionalErrorLeftSome: Either[Option[String], Int] = Either.left(Some("something went wrong")) + * scala> optionalErrorLeftSome.leftSequence + * res2: Option[Either[String, Int]] = Some(Left(something went wrong)) + * + * scala> val optionalErrorLeftNone: Either[Option[String], Int] = Either.left(None) + * scala> optionalErrorLeftNone.leftSequence + * res3: Option[Either[String,Int]] = None + * }}} + */ + @noop + def leftSequence[G[_], A, B](fgab: F[G[A], B])(implicit G: Applicative[G]): G[F[A, B]] = + bitraverse(fgab)(identity, G.pure(_)) } private[cats] trait ComposedBitraverse[F[_, _], G[_, _]] diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index bb939cca0e..c75ee126b5 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -149,4 +149,50 @@ import simulacrum.noop */ def flatTap[A, B](fa: F[A])(f: A => F[B]): F[A] = flatMap(fa)(a => as(f(a), a)) + + /** + * Like an infinite loop of >> calls. This is most useful effect loops + * that you want to run forever in for instance a server. + * + * This will be an infinite loop, or it will return an F[Nothing]. + * + * Be careful using this. + * For instance, a List of length k will produce a list of length k^n at iteration + * n. This means if k = 0, we return an empty list, if k = 1, we loop forever + * allocating single element lists, but if we have a k > 1, we will allocate + * exponentially increasing memory and very quickly OOM. + */ + @noop + def foreverM[A, B](fa: F[A]): F[B] = { + // allocate two things once for efficiency. + val leftUnit = Left(()) + val stepResult: F[Either[Unit, B]] = map(fa)(_ => leftUnit) + tailRecM(())(_ => stepResult) + } + + /** + * iterateForeverM is almost exclusively useful for effect types. For instance, + * A may be some state, we may take the current state, run some effect to get + * a new state and repeat. + */ + @noop + def iterateForeverM[A, B](a: A)(f: A => F[A]): F[B] = + tailRecM[A, B](a)(f.andThen { fa => + map(fa)(Left(_): Either[A, B]) + }) + + /** + * This repeats an F until we get defined values. This can be useful + * for polling type operations on State (or RNG) Monads, or in effect + * monads. + */ + @noop + def untilDefinedM[A](foa: F[Option[A]]): F[A] = { + val leftUnit: Either[Unit, A] = Left(()) + val feither: F[Either[Unit, A]] = map(foa) { + case None => leftUnit + case Some(a) => Right(a) + } + tailRecM(())(_ => feither) + } } diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 5b672a7d3d..681fa84d55 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -3,7 +3,7 @@ package cats import scala.collection.mutable import cats.instances.either._ import cats.kernel.CommutativeMonoid -import simulacrum.typeclass +import simulacrum.{noop, typeclass} import Foldable.sentinel /** @@ -269,6 +269,77 @@ import Foldable.sentinel if (ob.isDefined) Eval.now(ob) else lb }.value + /** + * Monadic version of `collectFirstSome`. + * + * If there are no elements, the result is `None`. `collectFirstSomeM` short-circuits, + * i.e. once a Some element is found, no further effects are produced. + * + * For example: + * {{{ + * scala> import cats.implicits._ + * scala> def parseInt(s: String): Either[String, Int] = Either.catchOnly[NumberFormatException](s.toInt).leftMap(_.getMessage) + * scala> val keys1 = List("1", "2", "4", "5") + * scala> val map1 = Map(4 -> "Four", 5 -> "Five") + * scala> Foldable[List].collectFirstSomeM(keys1)(parseInt(_) map map1.get) + * res0: scala.util.Either[String,Option[String]] = Right(Some(Four)) + * + * scala> val map2 = Map(6 -> "Six", 7 -> "Seven") + * scala> Foldable[List].collectFirstSomeM(keys1)(parseInt(_) map map2.get) + * res1: scala.util.Either[String,Option[String]] = Right(None) + * + * scala> val keys2 = List("1", "x", "4", "5") + * scala> Foldable[List].collectFirstSomeM(keys2)(parseInt(_) map map1.get) + * res2: scala.util.Either[String,Option[String]] = Left(For input string: "x") + * + * scala> val keys3 = List("1", "2", "4", "x") + * scala> Foldable[List].collectFirstSomeM(keys3)(parseInt(_) map map1.get) + * res3: scala.util.Either[String,Option[String]] = Right(Some(Four)) + * }}} + */ + @noop + def collectFirstSomeM[G[_], A, B](fa: F[A])(f: A => G[Option[B]])(implicit G: Monad[G]): G[Option[B]] = + G.tailRecM(Foldable.Source.fromFoldable(fa)(self))(_.uncons match { + case Some((a, src)) => + G.map(f(a)) { + case None => Left(src.value) + case s => Right(s) + } + case None => G.pure(Right(None)) + }) + + /** + * Tear down a subset of this structure using a `PartialFunction`. + *{{{ + * scala> import cats.implicits._ + * scala> val xs = List(1, 2, 3, 4) + * scala> Foldable[List].collectFold(xs) { case n if n % 2 == 0 => n } + * res0: Int = 6 + *}}} + */ + @noop + def collectFold[A, B](fa: F[A])(f: PartialFunction[A, B])(implicit B: Monoid[B]): B = + foldLeft(fa, B.empty)((acc, a) => B.combine(acc, f.applyOrElse(a, (_: A) => B.empty))) + + /** + * Tear down a subset of this structure using a `A => Option[M]`. + *{{{ + * scala> import cats.implicits._ + * scala> val xs = List(1, 2, 3, 4) + * scala> def f(n: Int): Option[Int] = if (n % 2 == 0) Some(n) else None + * scala> Foldable[List].collectFoldSome(xs)(f) + * res0: Int = 6 + *}}} + */ + def collectFoldSome[A, B](fa: F[A])(f: A => Option[B])(implicit B: Monoid[B]): B = + foldLeft(fa, B.empty)( + (acc, a) => + f(a) match { + case Some(x) => B.combine(acc, x) + case None => acc + } + ) + /** * Fold implemented using the given Monoid[A] instance. */ @@ -313,6 +384,22 @@ import Foldable.sentinel } } + /** + * Fold implemented by mapping `A` values into `B` in a context `G` and then + * combining them using the `MonoidK[G]` instance. + * + * {{{ + * scala> import cats._, cats.implicits._ + * scala> val f: Int => Endo[String] = i => (s => s + i) + * scala> val x: Endo[String] = Foldable[List].foldMapK(List(1, 2, 3))(f) + * scala> val a = x("foo") + * a: String = "foo321" + * }}} + * */ + @noop + def foldMapK[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: MonoidK[G]): G[B] = + foldMap(fa)(f)(G.algebra) + /** * Alias for [[foldM]]. */ @@ -423,6 +510,36 @@ import Foldable.sentinel if (f(a)) Now(Some(a)) else lb }.value + /** + * Find the first element matching the effectful predicate, if one exists. + * + * If there are no elements, the result is `None`. `findM` short-circuits, + * i.e. once an element is found, no further effects are produced. + * + * For example: + * {{{ + * scala> import cats.implicits._ + * scala> val list = List(1,2,3,4) + * scala> Foldable[List].findM(list)(n => (n >= 2).asRight[String]) + * res0: Either[String,Option[Int]] = Right(Some(2)) + * + * scala> Foldable[List].findM(list)(n => (n > 4).asRight[String]) + * res1: Either[String,Option[Int]] = Right(None) + * + * scala> Foldable[List].findM(list)(n => Either.cond(n < 3, n >= 2, "error")) + * res2: Either[String,Option[Int]] = Right(Some(2)) + * + * scala> Foldable[List].findM(list)(n => Either.cond(n < 3, false, "error")) + * res3: Either[String,Option[Int]] = Left(error) + * }}} + */ + @noop + def findM[G[_], A](fa: F[A])(p: A => G[Boolean])(implicit G: Monad[G]): G[Option[A]] = + G.tailRecM(Foldable.Source.fromFoldable(fa)(self))(_.uncons match { + case Some((a, src)) => G.map(p(a))(if (_) Right(Some(a)) else Left(src.value)) + case None => G.pure(Right(None)) + }) + /** * Check whether at least one element satisfies the predicate. * @@ -625,6 +742,84 @@ import Foldable.sentinel override def unorderedFoldMap[A, B: CommutativeMonoid](fa: F[A])(f: (A) => B): B = foldMap(fa)(f) + + /** + * Separate this Foldable into a Tuple by a separating function `A => H[B, C]` for some `Bifoldable[H]` + * Equivalent to `Functor#map` and then `Alternative#separate`. + * + * {{{ + * scala> import cats.implicits._, cats.Foldable, cats.data.Const + * scala> val list = List(1,2,3,4) + * scala> Foldable[List].partitionBifold(list)(a => ("value " + a.toString(), if (a % 2 == 0) -a else a)) + * res0: (List[String], List[Int]) = (List(value 1, value 2, value 3, value 4),List(1, -2, 3, -4)) + * scala> Foldable[List].partitionBifold(list)(a => Const[Int, Nothing with Any](a)) + * res1: (List[Int], List[Nothing with Any]) = (List(1, 2, 3, 4),List()) + * }}} + */ + @noop + def partitionBifold[H[_, _], A, B, C](fa: F[A])(f: A => H[B, C])(implicit A: Alternative[F], + H: Bifoldable[H]): (F[B], F[C]) = { + import cats.instances.tuple._ + + implicit val mb: Monoid[F[B]] = A.algebra[B] + implicit val mc: Monoid[F[C]] = A.algebra[C] + + foldMap[A, (F[B], F[C])](fa)( + a => H.bifoldMap[B, C, (F[B], F[C])](f(a))(b => (A.pure(b), A.empty[C]), c => (A.empty[B], A.pure(c))) + ) + } + + /** + * Separate this Foldable into a Tuple by an effectful separating function `A => G[H[B, C]]` for some `Bifoldable[H]` + * Equivalent to `Traverse#traverse` over `Alternative#separate` + * + * {{{ + * scala> import cats.implicits._, cats.Foldable, cats.data.Const + * scala> val list = List(1,2,3,4) + * `Const`'s second parameter is never instantiated, so we can use an impossible type: + * scala> Foldable[List].partitionBifoldM(list)(a => Option(Const[Int, Nothing with Any](a))) + * res0: Option[(List[Int], List[Nothing with Any])] = Some((List(1, 2, 3, 4),List())) + * }}} + */ + @noop + def partitionBifoldM[G[_], H[_, _], A, B, C]( + fa: F[A] + )(f: A => G[H[B, C]])(implicit A: Alternative[F], M: Monad[G], H: Bifoldable[H]): G[(F[B], F[C])] = { + import cats.instances.tuple._ + + implicit val mb: Monoid[F[B]] = A.algebra[B] + implicit val mc: Monoid[F[C]] = A.algebra[C] + + foldMapM[G, A, (F[B], F[C])](fa)( + a => + M.map(f(a)) { + H.bifoldMap[B, C, (F[B], F[C])](_)(b => (A.pure(b), A.empty[C]), c => (A.empty[B], A.pure(c))) + } + ) + } + + /** + * Separate this Foldable into a Tuple by an effectful separating function `A => G[Either[B, C]]` + * Equivalent to `Traverse#traverse` over `Alternative#separate` + * + * {{{ + * scala> import cats.implicits._, cats.Foldable, cats.Eval + * scala> val list = List(1,2,3,4) + * scala> val partitioned1 = Foldable[List].partitionEitherM(list)(a => if (a % 2 == 0) Eval.now(Either.left[String, Int](a.toString)) else Eval.now(Either.right[String, Int](a))) + * Since `Eval.now` yields a lazy computation, we need to force it to inspect the result: + * scala> partitioned1.value + * res0: (List[String], List[Int]) = (List(2, 4),List(1, 3)) + * scala> val partitioned2 = Foldable[List].partitionEitherM(list)(a => Eval.later(Either.right(a * 4))) + * scala> partitioned2.value + * res1: (List[Nothing], List[Int]) = (List(),List(4, 8, 12, 16)) + * }}} + */ + @noop + def partitionEitherM[G[_], A, B, C](fa: F[A])(f: A => G[Either[B, C]])(implicit A: Alternative[F], + M: Monad[G]): G[(F[B], F[C])] = { + import cats.instances.either._ + partitionBifoldM[G, Either, A, B, C](fa)(f)(A, M, Bifoldable[Either]) + } } object Foldable { diff --git a/core/src/main/scala/cats/Reducible.scala b/core/src/main/scala/cats/Reducible.scala index f41161b558..6bb748a413 100644 --- a/core/src/main/scala/cats/Reducible.scala +++ b/core/src/main/scala/cats/Reducible.scala @@ -1,7 +1,7 @@ package cats import cats.data.{Ior, NonEmptyList} -import simulacrum.typeclass +import simulacrum.{noop, typeclass} /** * Data structures that can be reduced to a summary value. @@ -54,6 +54,22 @@ import simulacrum.typeclass def reduceMap[A, B](fa: F[A])(f: A => B)(implicit B: Semigroup[B]): B = reduceLeftTo(fa)(f)((b, a) => B.combine(b, f(a))) + /** + * Apply `f` to each element of `fa` and combine them using the + * given `SemigroupK[G]`. + * + * {{{ + * scala> import cats._, cats.data._, cats.implicits._ + * scala> val f: Int => Endo[String] = i => (s => s + i) + * scala> val x: Endo[String] = Reducible[NonEmptyList].reduceMapK(NonEmptyList.of(1, 2, 3))(f) + * scala> val a = x("foo") + * a: String = "foo321" + * }}} + * */ + @noop + def reduceMapK[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: SemigroupK[G]): G[B] = + reduceLeftTo(fa)(f)((b, a) => G.combineK(b, f(a))) + /** * Apply `f` to the "initial element" of `fa` and combine it with * every other value using the given function `g`. diff --git a/core/src/main/scala/cats/TraverseFilter.scala b/core/src/main/scala/cats/TraverseFilter.scala index ea2848de63..0bf3f7330a 100644 --- a/core/src/main/scala/cats/TraverseFilter.scala +++ b/core/src/main/scala/cats/TraverseFilter.scala @@ -1,6 +1,6 @@ package cats -import simulacrum.typeclass +import simulacrum.{noop, typeclass} /** * `TraverseFilter`, also known as `Witherable`, represents list-like structures @@ -34,6 +34,18 @@ trait TraverseFilter[F[_]] extends FunctorFilter[F] { */ def traverseFilter[G[_], A, B](fa: F[A])(f: A => G[Option[B]])(implicit G: Applicative[G]): G[F[B]] + /** + * {{{ + * scala> import cats.implicits._ + * scala> val a: List[Either[String, Option[Int]]] = List(Right(Some(1)), Right(Some(5)), Right(Some(3))) + * scala> val b: Either[String, List[Int]] = TraverseFilter[List].sequenceFilter(a) + * b: Either[String, List[Int]] = Right(List(1, 5, 3)) + * }}} + * */ + @noop + def sequenceFilter[G[_], A](fgoa: F[G[Option[A]]])(implicit G: Applicative[G]): G[F[A]] = + traverseFilter(fgoa)(identity) + /** * * Filter values inside a `G` context. diff --git a/core/src/main/scala/cats/UnorderedFoldable.scala b/core/src/main/scala/cats/UnorderedFoldable.scala index 4336908d1f..683575f3e1 100644 --- a/core/src/main/scala/cats/UnorderedFoldable.scala +++ b/core/src/main/scala/cats/UnorderedFoldable.scala @@ -1,8 +1,8 @@ package cats -import cats.kernel.CommutativeMonoid -import simulacrum.typeclass import cats.instances.long._ +import cats.kernel.CommutativeMonoid +import simulacrum.{noop, typeclass} /** * `UnorderedFoldable` is like a `Foldable` for unordered containers. @@ -48,6 +48,27 @@ import cats.instances.long._ * Note: will not terminate for infinite-sized collections. */ def size[A](fa: F[A]): Long = unorderedFoldMap(fa)(_ => 1L) + + /** + * Count the number of elements in the structure that satisfy the given predicate. + * + * For example: + * {{{ + * scala> import cats.implicits._ + * scala> val map1 = Map[Int, String]() + * scala> val p1: String => Boolean = _.length > 0 + * scala> UnorderedFoldable[Map[Int, *]].count(map1)(p1) + * res0: Long = 0 + * + * scala> val map2 = Map(1 -> "hello", 2 -> "world", 3 -> "!") + * scala> val p2: String => Boolean = _.length > 1 + * scala> UnorderedFoldable[Map[Int, *]].count(map2)(p2) + * res1: Long = 2 + * }}} + */ + @noop + def count[A](fa: F[A])(p: A => Boolean): Long = + unorderedFoldMap(fa)(a => if (p(a)) 1L else 0L) } object UnorderedFoldable { diff --git a/core/src/main/scala/cats/syntax/applicativeError.scala b/core/src/main/scala/cats/syntax/applicativeError.scala index ed58a5cc2a..10125e46fe 100644 --- a/core/src/main/scala/cats/syntax/applicativeError.scala +++ b/core/src/main/scala/cats/syntax/applicativeError.scala @@ -1,7 +1,6 @@ package cats package syntax -import cats.data.Validated.{Invalid, Valid} import cats.data.{EitherT, Validated} import scala.reflect.ClassTag @@ -19,55 +18,22 @@ trait ApplicativeErrorSyntax { /** * Extension to ApplicativeError in a binary compat way */ -trait ApplicativeErrorExtension { - implicit final def catsSyntaxApplicativeErrorExtension[F[_], E]( +private[syntax] trait ApplicativeErrorExtension { + @deprecated("Use methods on ApplicativeError", "2.1.0-RC1") + final def catsSyntaxApplicativeErrorExtension[F[_], E]( F: ApplicativeError[F, E] ): ApplicativeErrorExtensionOps[F, E] = new ApplicativeErrorExtensionOps(F) } -final class ApplicativeErrorExtensionOps[F[_], E](F: ApplicativeError[F, E]) { +@deprecated("Use methods on ApplicativeError", "2.1.0-RC1") +final private[syntax] class ApplicativeErrorExtensionOps[F[_], E](F: ApplicativeError[F, E]) { - /** - * Convert from scala.Option - * - * Example: - * {{{ - * scala> import cats.implicits._ - * scala> import cats.ApplicativeError - * scala> val F = ApplicativeError[Either[String, *], String] - * - * scala> F.fromOption(Some(1), "Empty") - * res0: scala.Either[String, Int] = Right(1) - * - * scala> F.fromOption(Option.empty[Int], "Empty") - * res1: scala.Either[String, Int] = Left(Empty) - * }}} - */ - def fromOption[A](oa: Option[A], ifEmpty: => E): F[A] = - ApplicativeError.liftFromOption(oa, ifEmpty)(F) - - /** - * Convert from cats.data.Validated - * - * Example: - * {{{ - * scala> import cats.implicits._ - * scala> import cats.ApplicativeError - * - * scala> ApplicativeError[Option, Unit].fromValidated(1.valid[Unit]) - * res0: scala.Option[Int] = Some(1) - * - * scala> ApplicativeError[Option, Unit].fromValidated(().invalid[Int]) - * res1: scala.Option[Int] = None - * }}} - */ - def fromValidated[A](x: Validated[E, A]): F[A] = - x match { - case Invalid(e) => F.raiseError(e) - case Valid(a) => F.pure(a) - } + @deprecated("Use fromOption on ApplicativeError", "2.1.0-RC1") + private[syntax] def fromOption[A](oa: Option[A], ifEmpty: => E): F[A] = F.fromOption(oa, ifEmpty) + @deprecated("Use fromValidated on ApplicativeError", "2.1.0-RC1") + private[syntax] def fromValidated[A](x: Validated[E, A]): F[A] = F.fromValidated(x) } final class ApplicativeErrorIdOps[E](private val e: E) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/apply.scala b/core/src/main/scala/cats/syntax/apply.scala index b11501c15a..3f9f4b7763 100644 --- a/core/src/main/scala/cats/syntax/apply.scala +++ b/core/src/main/scala/cats/syntax/apply.scala @@ -49,10 +49,7 @@ final class IfApplyOps[F[_]](private val fcond: F[Boolean]) extends AnyVal { * * }}} */ - def ifA[A](ifTrue: F[A], ifFalse: F[A])(implicit F: Apply[F]): F[A] = { - def ite(b: Boolean)(ifTrue: A, ifFalse: A) = if (b) ifTrue else ifFalse - F.ap2(F.map(fcond)(ite))(ifTrue, ifFalse) - } + def ifA[A](ifTrue: F[A], ifFalse: F[A])(implicit F: Apply[F]): F[A] = F.ifA(fcond)(ifTrue, ifFalse) } final class ApplyOps[F[_], A](private val fa: F[A]) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/bitraverse.scala b/core/src/main/scala/cats/syntax/bitraverse.scala index 4e72c0b4ee..b09661f267 100644 --- a/core/src/main/scala/cats/syntax/bitraverse.scala +++ b/core/src/main/scala/cats/syntax/bitraverse.scala @@ -54,7 +54,7 @@ final private[syntax] class BitraverseOpsBinCompat0[F[_, _], A, B](val fab: F[A, * }}} */ def leftTraverse[G[_], C](f: A => G[C])(implicit F: Bitraverse[F], G: Applicative[G]): G[F[C, B]] = - F.bitraverse(fab)(f, G.pure(_)) + F.leftTraverse[G, A, B, C](fab)(f) } final class LeftNestedBitraverseOps[F[_, _], G[_], A, B](val fgab: F[G[A], B]) extends AnyVal { @@ -81,5 +81,5 @@ final class LeftNestedBitraverseOps[F[_, _], G[_], A, B](val fgab: F[G[A], B]) e * }}} */ def leftSequence(implicit F: Bitraverse[F], G: Applicative[G]): G[F[A, B]] = - F.bitraverse(fgab)(identity, G.pure(_)) + F.leftSequence(fgab) } diff --git a/core/src/main/scala/cats/syntax/flatMap.scala b/core/src/main/scala/cats/syntax/flatMap.scala index 7cf0e24daf..17f8e15e77 100644 --- a/core/src/main/scala/cats/syntax/flatMap.scala +++ b/core/src/main/scala/cats/syntax/flatMap.scala @@ -54,13 +54,7 @@ final class FlatMapOps[F[_], A](private val fa: F[A]) extends AnyVal { * allocating single element lists, but if we have a k > 1, we will allocate * exponentially increasing memory and very quickly OOM. */ - def foreverM[B](implicit F: FlatMap[F]): F[B] = { - // allocate two things once for efficiency. - val leftUnit = Left(()) - val stepResult: F[Either[Unit, B]] = F.map(fa)(_ => leftUnit) - F.tailRecM(())(_ => stepResult) - } - + def foreverM[B](implicit F: FlatMap[F]): F[B] = F.foreverM[A, B](fa) } final class FlattenOps[F[_], A](private val ffa: F[F[A]]) extends AnyVal { @@ -124,10 +118,7 @@ final class FlatMapIdOps[A](private val a: A) extends AnyVal { * A may be some state, we may take the current state, run some effect to get * a new state and repeat. */ - def iterateForeverM[F[_], B](f: A => F[A])(implicit F: FlatMap[F]): F[B] = - tailRecM[F, B](f.andThen { fa => - F.map(fa)(Left(_): Either[A, B]) - }) + def iterateForeverM[F[_], B](f: A => F[A])(implicit F: FlatMap[F]): F[B] = F.iterateForeverM[A, B](a)(f) } trait FlatMapOptionSyntax { @@ -142,12 +133,5 @@ final class FlatMapOptionOps[F[_], A](private val fopta: F[Option[A]]) extends A * for polling type operations on State (or RNG) Monads, or in effect * monads. */ - def untilDefinedM(implicit F: FlatMap[F]): F[A] = { - val leftUnit: Either[Unit, A] = Left(()) - val feither: F[Either[Unit, A]] = F.map(fopta) { - case None => leftUnit - case Some(a) => Right(a) - } - F.tailRecM(())(_ => feither) - } + def untilDefinedM(implicit F: FlatMap[F]): F[A] = F.untilDefinedM[A](fopta) } diff --git a/core/src/main/scala/cats/syntax/foldable.scala b/core/src/main/scala/cats/syntax/foldable.scala index 53157f0c99..a09b08754a 100644 --- a/core/src/main/scala/cats/syntax/foldable.scala +++ b/core/src/main/scala/cats/syntax/foldable.scala @@ -15,7 +15,8 @@ private[syntax] trait FoldableSyntaxBinCompat0 { } private[syntax] trait FoldableSyntaxBinCompat1 { - implicit final def catsSyntaxFoldableBinCompat0[F[_]](fa: Foldable[F]): FoldableOps1[F] = + @deprecated("Use methods on Foldable", "2.1.0-RC1") + final def catsSyntaxFoldableBinCompat0[F[_]](fa: Foldable[F]): FoldableOps1[F] = new FoldableOps1(fa) } @@ -134,14 +135,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal { * }}} */ def collectFirstSomeM[G[_], B](f: A => G[Option[B]])(implicit F: Foldable[F], G: Monad[G]): G[Option[B]] = - G.tailRecM(Foldable.Source.fromFoldable(fa))(_.uncons match { - case Some((a, src)) => - G.map(f(a)) { - case None => Left(src.value) - case s => Right(s) - } - case None => G.pure(Right(None)) - }) + F.collectFirstSomeM[G, A, B](fa)(f) /** * Find the first element matching the effectful predicate, if one exists. @@ -166,11 +160,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal { * res3: Either[String,Option[Int]] = Left(error) * }}} */ - def findM[G[_]](p: A => G[Boolean])(implicit F: Foldable[F], G: Monad[G]): G[Option[A]] = - G.tailRecM(Foldable.Source.fromFoldable(fa))(_.uncons match { - case Some((a, src)) => G.map(p(a))(if (_) Right(Some(a)) else Left(src.value)) - case None => G.pure(Right(None)) - }) + def findM[G[_]](p: A => G[Boolean])(implicit F: Foldable[F], G: Monad[G]): G[Option[A]] = F.findM[G, A](fa)(p) /** * Tear down a subset of this structure using a `PartialFunction`. @@ -181,8 +171,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal { * res0: Int = 6 *}}} */ - def collectFold[M](f: PartialFunction[A, M])(implicit F: Foldable[F], M: Monoid[M]): M = - F.foldLeft(fa, M.empty)((acc, a) => M.combine(acc, f.applyOrElse(a, (_: A) => M.empty))) + def collectFold[M](f: PartialFunction[A, M])(implicit F: Foldable[F], M: Monoid[M]): M = F.collectFold[A, M](fa)(f) /** * Tear down a subset of this structure using a `A => Option[M]`. @@ -190,18 +179,12 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal { * scala> import cats.implicits._ * scala> val xs = List(1, 2, 3, 4) * scala> def f(n: Int): Option[Int] = if (n % 2 == 0) Some(n) else None - * scala> xs.collectSomeFold(f) + * scala> xs.collectFoldSome(f) * res0: Int = 6 *}}} */ - def collectSomeFold[M](f: A => Option[M])(implicit F: Foldable[F], M: Monoid[M]): M = - F.foldLeft(fa, M.empty)( - (acc, a) => - f(a) match { - case Some(x) => M.combine(acc, x) - case None => acc - } - ) + @deprecated("Use collectFoldSome", "2.1.0-RC1") + def collectSomeFold[M](f: A => Option[M])(implicit F: Foldable[F], M: Monoid[M]): M = F.collectFoldSome[A, M](fa)(f) } final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal { @@ -238,8 +221,7 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal { * a: String = "foo321" * }}} * */ - def foldMapK[G[_], B](f: A => G[B])(implicit F: Foldable[F], G: MonoidK[G]): G[B] = - F.foldMap(fa)(f)(G.algebra) + def foldMapK[G[_], B](f: A => G[B])(implicit F: Foldable[F], G: MonoidK[G]): G[B] = F.foldMapK(fa)(f) /** * Separate this Foldable into a Tuple by an effectful separating function `A => H[B, C]` for some `Bifoldable[H]` @@ -257,10 +239,8 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal { */ def partitionBifold[H[_, _], B, C]( f: A => H[B, C] - )(implicit A: Alternative[F], F: Foldable[F], H: Bifoldable[H]): (F[B], F[C]) = { - import cats.syntax.foldable._ + )(implicit A: Alternative[F], F: Foldable[F], H: Bifoldable[H]): (F[B], F[C]) = F.partitionBifold[H, A, B, C](fa)(f)(A, H) - } /** * Separate this Foldable into a Tuple by an effectful separating function `A => G[H[B, C]]` for some `Bifoldable[H]` @@ -276,10 +256,8 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal { */ def partitionBifoldM[G[_], H[_, _], B, C]( f: A => G[H[B, C]] - )(implicit A: Alternative[F], F: Foldable[F], M: Monad[G], H: Bifoldable[H]): G[(F[B], F[C])] = { - import cats.syntax.foldable._ + )(implicit A: Alternative[F], F: Foldable[F], M: Monad[G], H: Bifoldable[H]): G[(F[B], F[C])] = F.partitionBifoldM[G, H, A, B, C](fa)(f)(A, M, H) - } /** * Separate this Foldable into a Tuple by an effectful separating function `A => G[Either[B, C]]` @@ -299,86 +277,26 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal { */ def partitionEitherM[G[_], B, C]( f: A => G[Either[B, C]] - )(implicit A: Alternative[F], F: Foldable[F], M: Monad[G]): G[(F[B], F[C])] = { - import cats.syntax.foldable._ + )(implicit A: Alternative[F], F: Foldable[F], M: Monad[G]): G[(F[B], F[C])] = F.partitionEitherM[G, A, B, C](fa)(f)(A, M) - } } +@deprecated("Use methods on Foldable", "2.1.0-RC1") final private[syntax] class FoldableOps1[F[_]](private val F: Foldable[F]) extends AnyVal { - /** - * Separate this Foldable into a Tuple by a separating function `A => H[B, C]` for some `Bifoldable[H]` - * Equivalent to `Functor#map` and then `Alternative#separate`. - * - * {{{ - * scala> import cats.implicits._, cats.Foldable, cats.data.Const - * scala> val list = List(1,2,3,4) - * scala> Foldable[List].partitionBifold(list)(a => ("value " + a.toString(), if (a % 2 == 0) -a else a)) - * res0: (List[String], List[Int]) = (List(value 1, value 2, value 3, value 4),List(1, -2, 3, -4)) - * scala> Foldable[List].partitionBifold(list)(a => Const[Int, Nothing with Any](a)) - * res1: (List[Int], List[Nothing with Any]) = (List(1, 2, 3, 4),List()) - * }}} - */ + @deprecated("Use partitionBifold on Foldable", "2.1.0-RC1") def partitionBifold[H[_, _], A, B, C](fa: F[A])(f: A => H[B, C])(implicit A: Alternative[F], - H: Bifoldable[H]): (F[B], F[C]) = { - import cats.instances.tuple._ + H: Bifoldable[H]): (F[B], F[C]) = + F.partitionBifold[H, A, B, C](fa)(f) - implicit val mb: Monoid[F[B]] = A.algebra[B] - implicit val mc: Monoid[F[C]] = A.algebra[C] - - F.foldMap[A, (F[B], F[C])](fa)( - a => H.bifoldMap[B, C, (F[B], F[C])](f(a))(b => (A.pure(b), A.empty[C]), c => (A.empty[B], A.pure(c))) - ) - } - - /** - * Separate this Foldable into a Tuple by an effectful separating function `A => G[H[B, C]]` for some `Bifoldable[H]` - * Equivalent to `Traverse#traverse` over `Alternative#separate` - * - * {{{ - * scala> import cats.implicits._, cats.Foldable, cats.data.Const - * scala> val list = List(1,2,3,4) - * `Const`'s second parameter is never instantiated, so we can use an impossible type: - * scala> Foldable[List].partitionBifoldM(list)(a => Option(Const[Int, Nothing with Any](a))) - * res0: Option[(List[Int], List[Nothing with Any])] = Some((List(1, 2, 3, 4),List())) - * }}} - */ + @deprecated("Use partitionBifoldM on Foldable", "2.1.0-RC1") def partitionBifoldM[G[_], H[_, _], A, B, C]( fa: F[A] - )(f: A => G[H[B, C]])(implicit A: Alternative[F], M: Monad[G], H: Bifoldable[H]): G[(F[B], F[C])] = { - import cats.instances.tuple._ - - implicit val mb: Monoid[F[B]] = A.algebra[B] - implicit val mc: Monoid[F[C]] = A.algebra[C] - - F.foldMapM[G, A, (F[B], F[C])](fa)( - a => - M.map(f(a)) { - H.bifoldMap[B, C, (F[B], F[C])](_)(b => (A.pure(b), A.empty[C]), c => (A.empty[B], A.pure(c))) - } - ) - } + )(f: A => G[H[B, C]])(implicit A: Alternative[F], M: Monad[G], H: Bifoldable[H]): G[(F[B], F[C])] = + F.partitionBifoldM[G, H, A, B, C](fa)(f) - /** - * Separate this Foldable into a Tuple by an effectful separating function `A => G[Either[B, C]]` - * Equivalent to `Traverse#traverse` over `Alternative#separate` - * - * {{{ - * scala> import cats.implicits._, cats.Foldable, cats.Eval - * scala> val list = List(1,2,3,4) - * scala> val partitioned1 = Foldable[List].partitionEitherM(list)(a => if (a % 2 == 0) Eval.now(Either.left[String, Int](a.toString)) else Eval.now(Either.right[String, Int](a))) - * Since `Eval.now` yields a lazy computation, we need to force it to inspect the result: - * scala> partitioned1.value - * res0: (List[String], List[Int]) = (List(2, 4),List(1, 3)) - * scala> val partitioned2 = Foldable[List].partitionEitherM(list)(a => Eval.later(Either.right(a * 4))) - * scala> partitioned2.value - * res1: (List[Nothing], List[Int]) = (List(),List(4, 8, 12, 16)) - * }}} - */ + @deprecated("Use partitionEitherM on Foldable", "2.1.0-RC1") def partitionEitherM[G[_], A, B, C](fa: F[A])(f: A => G[Either[B, C]])(implicit A: Alternative[F], - M: Monad[G]): G[(F[B], F[C])] = { - import cats.instances.either._ - partitionBifoldM[G, Either, A, B, C](fa)(f)(A, M, Bifoldable[Either]) - } + M: Monad[G]): G[(F[B], F[C])] = + F.partitionEitherM[G, A, B, C](fa)(f) } diff --git a/core/src/main/scala/cats/syntax/reducible.scala b/core/src/main/scala/cats/syntax/reducible.scala index 3e5904ef52..76e1ff1faf 100644 --- a/core/src/main/scala/cats/syntax/reducible.scala +++ b/core/src/main/scala/cats/syntax/reducible.scala @@ -29,6 +29,5 @@ final class ReducibleOps0[F[_], A](private val fa: F[A]) extends AnyVal { * a: String = "foo321" * }}} * */ - def reduceMapK[G[_], B](f: A => G[B])(implicit F: Reducible[F], G: SemigroupK[G]): G[B] = - F.reduceLeftTo(fa)(f)((b, a) => G.combineK(b, f(a))) + def reduceMapK[G[_], B](f: A => G[B])(implicit F: Reducible[F], G: SemigroupK[G]): G[B] = F.reduceMapK[G, A, B](fa)(f) } diff --git a/core/src/main/scala/cats/syntax/traverseFilter.scala b/core/src/main/scala/cats/syntax/traverseFilter.scala index 9a00baba89..36c57b62a7 100644 --- a/core/src/main/scala/cats/syntax/traverseFilter.scala +++ b/core/src/main/scala/cats/syntax/traverseFilter.scala @@ -18,5 +18,5 @@ final class SequenceFilterOps[F[_], G[_], A](private val fgoa: F[G[Option[A]]]) * b: Either[String, List[Int]] = Right(List(1, 5, 3)) * }}} * */ - def sequenceFilter(implicit F: TraverseFilter[F], G: Applicative[G]): G[F[A]] = F.traverseFilter(fgoa)(identity) + def sequenceFilter(implicit F: TraverseFilter[F], G: Applicative[G]): G[F[A]] = F.sequenceFilter(fgoa) } diff --git a/core/src/main/scala/cats/syntax/unorderedFoldable.scala b/core/src/main/scala/cats/syntax/unorderedFoldable.scala index 567780c529..99f2ee1a3f 100644 --- a/core/src/main/scala/cats/syntax/unorderedFoldable.scala +++ b/core/src/main/scala/cats/syntax/unorderedFoldable.scala @@ -1,8 +1,6 @@ package cats package syntax -import cats.instances.long._ - trait UnorderedFoldableSyntax extends UnorderedFoldable.ToUnorderedFoldableOps { implicit final def catsSyntaxUnorderedFoldableOps[F[_]: UnorderedFoldable, A](fa: F[A]): UnorderedFoldableOps[F, A] = new UnorderedFoldableOps[F, A](fa) @@ -28,5 +26,5 @@ final class UnorderedFoldableOps[F[_], A](private val fa: F[A]) extends AnyVal { * }}} */ def count(p: A => Boolean)(implicit F: UnorderedFoldable[F]): Long = - F.unorderedFoldMap(fa)(a => if (p(a)) 1L else 0L) + F.count(fa)(p) } diff --git a/core/src/main/scala/cats/syntax/validated.scala b/core/src/main/scala/cats/syntax/validated.scala index 29f1b4ad55..758818a36f 100644 --- a/core/src/main/scala/cats/syntax/validated.scala +++ b/core/src/main/scala/cats/syntax/validated.scala @@ -21,7 +21,7 @@ trait ValidatedExtensionSyntax { final class ValidatedExtension[E, A](private val self: Validated[E, A]) extends AnyVal { def liftTo[F[_]](implicit F: ApplicativeError[F, _ >: E]): F[A] = - new ApplicativeErrorExtensionOps(F).fromValidated(self) + F.fromValidated(self) } private[syntax] trait ValidatedSyntaxBincompat0 {