diff --git a/bench/src/main/scala/cats/bench/TraverseStrategyBench.scala b/bench/src/main/scala/cats/bench/TraverseStrategyBench.scala new file mode 100644 index 0000000000..5822c4f13d --- /dev/null +++ b/bench/src/main/scala/cats/bench/TraverseStrategyBench.scala @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats.bench + +import cats.{Eval, Traverse} +import cats.instances.list.* +import cats.instances.vector.* +import cats.instances.either.* +import org.openjdk.jmh.annotations.{Benchmark, Param, Scope, Setup, State} +import org.openjdk.jmh.infra.Blackhole + +@State(Scope.Benchmark) +class TraverseStrategyBench { + val listT: Traverse[List] = Traverse[List] + val vectorT: Traverse[Vector] = Traverse[Vector] + + @Param(Array("100", "1000", "10000")) + var length: Int = _ + + var list: List[Int] = _ + var vector: Vector[Int] = _ + var evalVector: Vector[Eval[Int]] = _ + + @Setup + def setup(): Unit = { + list = (0 until length).toList + vector = (0 until length).toVector + evalVector = (0 until length).toVector.map(Eval.now) + } + + @Benchmark + def traverseEvalList(bh: Blackhole): Unit = { + val result = listT.traverse(list) { i => Eval.now(i) } + bh.consume(result.value) + } + + @Benchmark + def traverseVoidEvalList(bh: Blackhole): Unit = { + val result = listT.traverseVoid(list) { i => Eval.now(i) } + bh.consume(result.value) + } + + @Benchmark + def traverseEitherList(bh: Blackhole): Unit = { + val result = listT.traverse(list) { i => + if (i > length + 100) Left("Error") else Right(i) + } + bh.consume(result match { + case Right(l) => l.headOption + case Left(_) => None + }) + } + + @Benchmark + def traverseVoidEitherList(bh: Blackhole): Unit = { + val result = listT.traverseVoid(list) { i => + if (i > length + 100) Left("Error") else Right(i) + } + bh.consume(result match { + case Right(_) => true + case Left(_) => false + }) + } + + @Benchmark + def sequenceEvalVector(bh: Blackhole): Unit = { + val result = vectorT.sequence(evalVector) + bh.consume(result.value) + } +} diff --git a/core/src/main/scala/cats/Apply.scala b/core/src/main/scala/cats/Apply.scala index f153632841..0d79aff3a5 100644 --- a/core/src/main/scala/cats/Apply.scala +++ b/core/src/main/scala/cats/Apply.scala @@ -246,11 +246,92 @@ trait Apply[F[_]] extends Functor[F] with InvariantSemigroupal[F] with ApplyArit def ite(b: Boolean)(ifTrue: A, ifFalse: A) = if (b) ifTrue else ifFalse ap2(map(fcond)(ite))(ifTrue, ifFalse) } + + def traverseStrategy: Apply.TraverseStrategy[F] = + Apply.TraverseStrategy.viaEval(this) } object Apply { abstract private[cats] class AbstractApply[F[_]] extends Apply[F] + /** + * This type can be used by those implementing traverse-like + * functions where you want to respect the potential laziness + * or short-circuiting built into F if it exists without + * having to pay an additional cost to wrap in Eval[F[A]] + */ + trait TraverseStrategy[F[_]] extends Serializable { self => + type Rhs[_] + + def map2[A, B, C](left: Rhs[A], right: Rhs[B])(fn: (A, B) => C): Rhs[C] + def applyToRhs[A, B](fn: A => F[B], arg: A): Rhs[B] + def applyOnRhs[A, B](fn: A => Rhs[B], arg: A): Rhs[B] + def rhsToF[A](r: Rhs[A]): F[A] + def mapRhs[A, B](r: Rhs[A])(fn: A => B): Rhs[B] + } + + object TraverseStrategy { + final case class Direct[F[_]](F: Apply[F]) extends TraverseStrategy[F] { + type Rhs[A] = F[A] + def map2[A, B, C](left: Rhs[A], right: Rhs[B])(fn: (A, B) => C): Rhs[C] = + F.map2(left, right)(fn) + + def applyToRhs[A, B](fn: A => F[B], arg: A): Rhs[B] = fn(arg) + def applyOnRhs[A, B](fn: A => Rhs[B], arg: A): Rhs[B] = fn(arg) + def rhsToF[A](r: Rhs[A]): F[A] = r + def mapRhs[A, B](r: Rhs[A])(fn: A => B): Rhs[B] = F.map(r)(fn) + } + + final case class ViaEval[F[_]](F: Apply[F]) extends TraverseStrategy[F] { + type Rhs[A] = Eval[F[A]] + def map2[A, B, C](left: Rhs[A], right: Rhs[B])(fn: (A, B) => C): Rhs[C] = + left.flatMap { fa => + F.map2Eval(fa, right)(fn) + } + + def applyToRhs[A, B](fn: A => F[B], arg: A): Rhs[B] = Eval.always(fn(arg)) + def applyOnRhs[A, B](fn: A => Rhs[B], arg: A): Rhs[B] = Eval.defer(fn(arg)) + def rhsToF[A](r: Rhs[A]): F[A] = r.value + def mapRhs[A, B](r: Rhs[A])(fn: A => B): Rhs[B] = r.map { fa => F.map(fa)(fn) } + } + + /** + * This strategy directly does map2 on the type F. + * + * This is useful for Fs that are already lazy, or + * have no short-circuiting properties + */ + def direct[F[_]](a: Apply[F]): TraverseStrategy[F] = + Direct(a) + + /** + * This strategy wraps F[A] as Eval[F[A]] which + * allows laziness in traverse if F can possibly stop early. + * This is useful for strict error types (e.g. Either, Option) + * or possibly empty collections which are similar to strict error types + */ + def viaEval[F[_]](a: Apply[F]): TraverseStrategy[F] = + ViaEval(a) + + def composeOuter[F[_], G[_]](self: TraverseStrategy[F], that: Apply[G]): TraverseStrategy[λ[x => F[G[x]]]] = + new TraverseStrategy[λ[x => F[G[x]]]] { + type Rhs[A] = self.Rhs[G[A]] + + def map2[A, B, C](left: Rhs[A], right: Rhs[B])(fn: (A, B) => C): Rhs[C] = + self.map2(left, right) { (ga, gb) => that.map2(ga, gb)(fn) } + def applyToRhs[A, B](fn: A => F[G[B]], arg: A): Rhs[B] = + self.applyToRhs(fn, arg) + + def applyOnRhs[A, B](fn: A => Rhs[B], arg: A): Rhs[B] = + self.applyOnRhs(fn, arg) + + def rhsToF[A](r: Rhs[A]): F[G[A]] = + self.rhsToF(r) + def mapRhs[A, B](r: Rhs[A])(fn: A => B): Rhs[B] = + self.mapRhs(r) { ga => that.map(ga)(fn) } + } + } + /** * This semigroup uses a product operation to combine `F`s. * If the `Apply[F].product` results in larger `F` (i.e. when `F` is a `List`), diff --git a/core/src/main/scala/cats/Composed.scala b/core/src/main/scala/cats/Composed.scala index b20cf3b419..8a89309c92 100644 --- a/core/src/main/scala/cats/Composed.scala +++ b/core/src/main/scala/cats/Composed.scala @@ -64,6 +64,8 @@ private[cats] trait ComposedApply[F[_], G[_]] extends Apply.AbstractApply[λ[α override def product[A, B](fga: F[G[A]], fgb: F[G[B]]): F[G[(A, B)]] = F.map2(fga, fgb)(G.product) + + override def traverseStrategy = Apply.TraverseStrategy.composeOuter(F.traverseStrategy, G) } private[cats] trait ComposedApplicative[F[_], G[_]] extends ComposedApply[F, G] with Applicative[λ[α => F[G[α]]]] { diff --git a/core/src/main/scala/cats/Eval.scala b/core/src/main/scala/cats/Eval.scala index f3b4f6776d..06ee0da48a 100644 --- a/core/src/main/scala/cats/Eval.scala +++ b/core/src/main/scala/cats/Eval.scala @@ -398,6 +398,8 @@ sealed abstract private[cats] class EvalInstances extends EvalInstances0 { def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = Later(f(fa)) override def unit: Eval[Unit] = Eval.Unit override def void[A](a: Eval[A]): Eval[Unit] = Eval.Unit + // Eval is already lazy + override val traverseStrategy: Apply.TraverseStrategy[Eval] = Apply.TraverseStrategy.direct(this) } implicit val catsDeferForEval: Defer[Eval] = diff --git a/core/src/main/scala/cats/data/Chain.scala b/core/src/main/scala/cats/data/Chain.scala index b54f124700..7bf401ec4c 100644 --- a/core/src/main/scala/cats/data/Chain.scala +++ b/core/src/main/scala/cats/data/Chain.scala @@ -1194,43 +1194,43 @@ object Chain extends ChainInstances with ChainCompanionCompat { else { // we branch out by this factor val width = 128 + val strat = G.traverseStrategy + val toG: A => G[List[B]] = { a => G.map(f(a))(_ :: Nil) } + val consB: (B, List[B]) => List[B] = _ :: _ + val concatChain: (Chain[B], Chain[B]) => Chain[B] = _.concat(_) + // By making a tree here we don't blow the stack // even if the List is very long // by construction, this is never called with start == end - def loop(start: Int, end: Int): Eval[G[Chain[B]]] = + def loop(start: Int, end: Int): strat.Rhs[Chain[B]] = if (end - start <= width) { - // Here we are at the leafs of the trees - // we don't use map2Eval since it is always - // at most width in size. - var flist = Eval.later(G.map(f(as(end - 1)))(_ :: Nil)) + var flist = strat.applyToRhs(toG, as(end - 1)) var idx = end - 2 while (start <= idx) { - val a = as(idx) - // don't capture a var in the defer - val right = flist - flist = Eval.defer(G.map2Eval(f(a), right)(_ :: _)) + val leftRhs = strat.applyToRhs(f, as(idx)) + flist = strat.map2(leftRhs, flist)(consB) idx = idx - 1 } - flist.map { glist => G.map(glist)(Chain.fromSeq(_)) } + strat.mapRhs(flist)(Chain.fromSeq(_)) } else { // we have width + 1 or more nodes left val step = (end - start) / width - var fchain = Eval.defer(loop(start, start + step)) + var fchain = loop(start, start + step) var start0 = start + step var end0 = start0 + step while (start0 < end) { val end1 = math.min(end, end0) val right = loop(start0, end1) - fchain = fchain.flatMap(G.map2Eval(_, right)(_.concat(_))) + fchain = strat.map2(fchain, right)(concatChain) start0 = start0 + step end0 = end0 + step } fchain } - loop(0, as.size).value + strat.rhsToF(loop(0, as.size)) } def traverseFilterViaChain[G[_], A, B]( @@ -1240,49 +1240,52 @@ object Chain extends ChainInstances with ChainCompanionCompat { else { // we branch out by this factor val width = 128 + val strat = G.traverseStrategy + val toG: A => G[List[B]] = { (a: A) => + G.map(f(a)) { optB => + if (optB.isDefined) optB.get :: Nil + else Nil + } + } + val consB: (Option[B], List[B]) => List[B] = { (optB, tail) => + if (optB.isDefined) optB.get :: tail + else tail + } + val concatChain: (Chain[B], Chain[B]) => Chain[B] = _.concat(_) // By making a tree here we don't blow the stack // even if the List is very long // by construction, this is never called with start == end - def loop(start: Int, end: Int): Eval[G[Chain[B]]] = + def loop(start: Int, end: Int): strat.Rhs[Chain[B]] = if (end - start <= width) { - // Here we are at the leafs of the trees - // we don't use map2Eval since it is always - // at most width in size. - var flist = Eval.later(G.map(f(as(end - 1))) { optB => - if (optB.isDefined) optB.get :: Nil - else Nil - }) + var flist = strat.applyToRhs(toG, as(end - 1)) var idx = end - 2 while (start <= idx) { - val a = as(idx) + val fa = strat.applyToRhs(f, as(idx)) // don't capture a var in the defer val right = flist - flist = Eval.defer(G.map2Eval(f(a), right) { (optB, tail) => - if (optB.isDefined) optB.get :: tail - else tail - }) + flist = strat.map2(fa, right)(consB) idx = idx - 1 } - flist.map { glist => G.map(glist)(Chain.fromSeq(_)) } + strat.mapRhs(flist)(Chain.fromSeq(_)) } else { // we have width + 1 or more nodes left val step = (end - start) / width - var fchain = Eval.defer(loop(start, start + step)) + var fchain = loop(start, start + step) var start0 = start + step var end0 = start0 + step while (start0 < end) { val end1 = math.min(end, end0) val right = loop(start0, end1) - fchain = fchain.flatMap(G.map2Eval(_, right)(_.concat(_))) + fchain = strat.map2(fchain, right)(concatChain) start0 = start0 + step end0 = end0 + step } fchain } - loop(0, as.size).value + strat.rhsToF(loop(0, as.size)) } private class ChainIterator[A](self: NonEmpty[A]) extends Iterator[A] { diff --git a/core/src/main/scala/cats/data/IndexedStateT.scala b/core/src/main/scala/cats/data/IndexedStateT.scala index ead627a2c7..90c686c270 100644 --- a/core/src/main/scala/cats/data/IndexedStateT.scala +++ b/core/src/main/scala/cats/data/IndexedStateT.scala @@ -467,6 +467,9 @@ sealed abstract private[data] class IndexedStateTMonad[F[_], S] } } ) + + // IndexedStateT is already lazy and sequential + override val traverseStrategy = Apply.TraverseStrategy.direct(this) } sealed abstract private[data] class IndexedStateTSemigroupK[F[_], SA, SB] diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index 6bba68ed6e..9386d2051b 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -733,6 +733,45 @@ private[data] trait KleisliApply[F[_], A] extends Apply.AbstractApply[Kleisli[F, override def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = Kleisli(a => F.product(fb.run(a), fc.run(a))) + + // use the same traverse strategy as F0 + override lazy val traverseStrategy = { + val stratF = F.traverseStrategy + + stratF match { + case Apply.TraverseStrategy.Direct(_) => + // if the inner is direct, be direct here: + Apply.TraverseStrategy.Direct(this) + case _ => + new Apply.TraverseStrategy[Kleisli[F, A, *]] { + type Rhs[B] = A => stratF.Rhs[B] + + def map2[A0, B, C](left: Rhs[A0], right: Rhs[B])(fn: (A0, B) => C): Rhs[C] = { a => + val l = stratF.applyOnRhs(left, a) + val r = stratF.applyOnRhs(right, a) + stratF.map2(l, r)(fn) + } + + def applyToRhs[A0, B](fn: A0 => Kleisli[F, A, B], arg: A0): Rhs[B] = { + lazy val k: A => F[B] = fn(arg).run + + { (a: A) => stratF.applyToRhs(k, a) } + } + + def applyOnRhs[A0, B](fn: A0 => Rhs[B], arg: A0): Rhs[B] = { + lazy val k: A => stratF.Rhs[B] = fn(arg) + + { (a: A) => stratF.applyOnRhs(k, a) } + } + + def rhsToF[A0](r: Rhs[A0]): Kleisli[F, A, A0] = + Kleisli(AndThen(r).andThen(stratF.rhsToF(_))) + + def mapRhs[A0, B](r: Rhs[A0])(fn: A0 => B): Rhs[B] = + AndThen(r).andThen { (stratRhs: stratF.Rhs[A0]) => stratF.mapRhs(stratRhs)(fn) } + } + } + } } private[data] trait KleisliFunctor[F[_], A] extends Functor[Kleisli[F, A, *]] { diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 01c64dffaf..0e714a15a1 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -786,6 +786,9 @@ object NonEmptyList extends NonEmptyListInstances { override def product[A, B](fa: ZipNonEmptyList[A], fb: ZipNonEmptyList[B]): ZipNonEmptyList[(A, B)] = ZipNonEmptyList(fa.value.zipWith(fb.value) { case (a, b) => (a, b) }) + + // We can never stop early on a nonempty list + override val traverseStrategy: Apply.TraverseStrategy[ZipNonEmptyList] = Apply.TraverseStrategy.direct(this) } @deprecated("Use catsDataEqForZipNonEmptyList", "2.0.0-RC2") @@ -851,6 +854,9 @@ sealed abstract private[data] class NonEmptyListInstances extends NonEmptyListIn def flatMap[A, B](fa: NonEmptyList[A])(f: A => NonEmptyList[B]): NonEmptyList[B] = fa.flatMap(f) + // We can never stop early on a nonempty list + override val traverseStrategy: Apply.TraverseStrategy[NonEmptyList] = Apply.TraverseStrategy.direct(this) + def coflatMap[A, B](fa: NonEmptyList[A])(f: NonEmptyList[A] => B): NonEmptyList[B] = fa.coflatMap(f) diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index 93822ff91a..84796290bd 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -448,6 +448,9 @@ sealed abstract private[data] class NonEmptyVectorInstances extends NonEmptyVect def flatMap[A, B](fa: NonEmptyVector[A])(f: A => NonEmptyVector[B]): NonEmptyVector[B] = fa.flatMap(f) + // We can never stop early on a nonempty vector + override val traverseStrategy: Apply.TraverseStrategy[NonEmptyVector] = Apply.TraverseStrategy.direct(this) + def coflatMap[A, B](fa: NonEmptyVector[A])(f: NonEmptyVector[A] => B): NonEmptyVector[B] = { @tailrec def consume(as: Vector[A], buf: VectorBuilder[B]): Vector[B] = as match { @@ -638,6 +641,9 @@ object NonEmptyVector extends NonEmptyVectorInstances with Serializable { override def product[A, B](fa: ZipNonEmptyVector[A], fb: ZipNonEmptyVector[B]): ZipNonEmptyVector[(A, B)] = ZipNonEmptyVector(fa.value.zipWith(fb.value) { case (a, b) => (a, b) }) + + // We can never stop early on a nonempty vector + override val traverseStrategy: Apply.TraverseStrategy[ZipNonEmptyVector] = Apply.TraverseStrategy.direct(this) } @deprecated("Use catsDataEqForZipNonEmptyVector", "2.0.0-RC2") diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index 199b363797..786e926303 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -1113,6 +1113,9 @@ private[data] class ValidatedApplicative[E: Semigroup] fa.product(fb)(Semigroup[E]) override def unit: Validated[E, Unit] = Validated.validUnit + + // We accumulate all errors so there is no short circuiting here + override val traverseStrategy = Apply.TraverseStrategy.direct(this) } private[data] trait ValidatedFunctions { diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 8a6279a2d5..65ab8477cc 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -631,6 +631,35 @@ sealed private[data] trait WriterTApply[F[_], L] override def product[A, B](fa: WriterT[F, L, A], fb: WriterT[F, L, B]): WriterT[F, L, (A, B)] = WriterT(F0.map(F0.product(fa.run, fb.run)) { case ((l1, a), (l2, b)) => (L0.combine(l1, l2), (a, b)) }) + + // use the same traverse strategy as F0 + override lazy val traverseStrategy = { + val stratF = F0.traverseStrategy + + stratF match { + case Apply.TraverseStrategy.Direct(_) => + Apply.TraverseStrategy.Direct(this) + case _ => + new Apply.TraverseStrategy[WriterT[F, L, *]] { + type Rhs[A] = stratF.Rhs[(L, A)] + + def map2[A, B, C](left: Rhs[A], right: Rhs[B])(fn: (A, B) => C): Rhs[C] = + stratF.map2(left, right) { case ((l1, a), (l2, b)) => (L0.combine(l1, l2), fn(a, b)) } + + def applyToRhs[A, B](fn: A => WriterT[F, L, B], arg: A): Rhs[B] = + stratF.applyToRhs(fn.andThen(_.run), arg) + + def applyOnRhs[A, B](fn: A => Rhs[B], arg: A): Rhs[B] = + stratF.applyOnRhs(fn, arg) + + def rhsToF[A](r: Rhs[A]): WriterT[F, L, A] = + WriterT(stratF.rhsToF(r)) + + def mapRhs[A, B](r: Rhs[A])(fn: A => B): Rhs[B] = + stratF.mapRhs(r) { case (l, a) => (l, fn(a)) } + } + } + } } sealed private[data] trait WriterTFlatMap1[F[_], L] diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index ab2115c040..1808623e69 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -33,8 +33,9 @@ import scala.collection.mutable.ListBuffer trait ListInstances extends cats.kernel.instances.ListInstances { - implicit val catsStdInstancesForList - : Traverse[List] & Alternative[List] & Monad[List] & CoflatMap[List] & Align[List] = + implicit val catsStdInstancesForList: Traverse[List] & Alternative[ + List + ] & Monad[List] & CoflatMap[List] & Align[List] = new FlatMap.AbstractFoldableFlatMap[List] with Traverse[List] with Alternative[List] @@ -46,12 +47,16 @@ trait ListInstances extends cats.kernel.instances.ListInstances { def combineK[A](x: List[A], y: List[A]): List[A] = x ::: y - override def combineAllOptionK[A](as: IterableOnce[List[A]]): Option[List[A]] = { + override def combineAllOptionK[A]( + as: IterableOnce[List[A]] + ): Option[List[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(appendAll(iter, List.newBuilder[A]).result()) + if (iter.isEmpty) None + else Some(appendAll(iter, List.newBuilder[A]).result()) } - override def fromIterableOnce[A](as: IterableOnce[A]): List[A] = as.iterator.toList + override def fromIterableOnce[A](as: IterableOnce[A]): List[A] = + as.iterator.toList override def prependK[A](a: A, fa: List[A]): List[A] = a :: fa @@ -65,13 +70,18 @@ trait ListInstances extends cats.kernel.instances.ListInstances { def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] = fa.flatMap(f) - override def map2[A, B, Z](fa: List[A], fb: List[B])(f: (A, B) => Z): List[Z] = + override def map2[A, B, Z](fa: List[A], fb: List[B])( + f: (A, B) => Z + ): List[Z] = if (fb.isEmpty) Nil // do O(1) work if fb is empty - else fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty + else + fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty private[this] val evalNil: Eval[List[Nothing]] = Eval.now(Nil) - override def map2Eval[A, B, Z](fa: List[A], fb: Eval[List[B]])(f: (A, B) => Z): Eval[List[Z]] = + override def map2Eval[A, B, Z](fa: List[A], fb: Eval[List[B]])( + f: (A, B) => Z + ): Eval[List[Z]] = if (fa.isEmpty) evalNil // no need to evaluate fb else fb.map(fb => map2(fa, fb)(f)) @@ -103,7 +113,9 @@ trait ListInstances extends cats.kernel.instances.ListInstances { def foldLeft[A, B](fa: List[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) - def foldRight[A, B](fa: List[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + def foldRight[A, B](fa: List[A], lb: Eval[B])( + f: (A, Eval[B]) => Eval[B] + ): Eval[B] = { def loop(as: List[A]): Eval[B] = as match { case Nil => lb @@ -112,10 +124,14 @@ trait ListInstances extends cats.kernel.instances.ListInstances { Eval.defer(loop(fa)) } - override def foldMap[A, B](fa: List[A])(f: A => B)(implicit B: Monoid[B]): B = + override def foldMap[A, B](fa: List[A])(f: A => B)(implicit + B: Monoid[B] + ): B = B.combineAll(fa.iterator.map(f)) - override def foldMapK[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: MonoidK[G]): G[B] = { + override def foldMapK[G[_], A, B]( + fa: List[A] + )(f: A => G[B])(implicit G: MonoidK[G]): G[B] = { def loop(fa: List[A]): Eval[G[B]] = fa match { case head :: tl => G.combineKEval(f(head), Eval.defer(loop(tl))) @@ -124,12 +140,15 @@ trait ListInstances extends cats.kernel.instances.ListInstances { loop(fa).value } - def traverse[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] = + def traverse[G[_], A, B]( + fa: List[A] + )(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] = if (fa.isEmpty) G.pure(Nil) else G match { - case x: StackSafeMonad[G] => x.map(Traverse.traverseDirectly[G, A, B](fa)(f)(x))(_.toList) - case _ => + case x: StackSafeMonad[G] => + x.map(Traverse.traverseDirectly[G, A, B](fa)(f)(x))(_.toList) + case _ => G.map(Chain.traverseViaChain { val as = collection.mutable.ArrayBuffer[A]() as ++= fa @@ -137,48 +156,37 @@ trait ListInstances extends cats.kernel.instances.ListInstances { }(f))(_.toList) } - /** - * This avoids making a very deep stack by building a tree instead - */ - override def traverseVoid[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = { - G match { - case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa)(f)(x) - case _ => - // the cost of this is O(size log size) - // c(n) = n + 2 * c(n/2) = n + 2(n/2 log (n/2)) = n + n (logn - 1) = n log n - // invariant: size >= 1 - def runHalf(size: Int, fa: List[A]): Eval[G[Unit]] = - if (size > 1) { - val leftSize = size / 2 - val rightSize = size - leftSize - val (leftL, rightL) = fa.splitAt(leftSize) - runHalf(leftSize, leftL) - .flatMap { left => - val right = runHalf(rightSize, rightL) - G.map2Eval(left, right) { (_, _) => () } - } - } else { - // avoid pattern matching when we know that there is only one element - val a = fa.head - // we evaluate this at most one time, - // always is a bit cheaper in such cases - // - // Here is the point of the laziness using Eval: - // we avoid calling f(a) or G.void in the - // event that the computation has already - // failed. We do not use laziness to avoid - // traversing fa, which we will do fully - // in all cases. - Eval.always { - val gb = f(a) - G.void(gb) - } - } + /** This avoids making a very deep stack by building a tree instead + */ + override def traverseVoid[G[_], A, B]( + fa: List[A] + )(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = { + val unitFn: (Unit, Unit) => Unit = (_, _) => () + val toGUnit: A => G[Unit] = { a => G.void(f(a)) } + val strat = G.traverseStrategy + + // the cost of this is O(size log size) + // c(n) = n + 2 * c(n/2) = n + 2(n/2 log (n/2)) = n + n (logn - 1) = n log n + // invariant: size >= 1 + def runHalf(size: Int, fa: List[A]): strat.Rhs[Unit] = + if (size > 1) { + val leftSize = size / 2 + val rightSize = size - leftSize + val (leftL, rightL) = fa.splitAt(leftSize) + val lres = + strat.applyOnRhs[List[A], Unit](runHalf(leftSize, _), leftL) + val rres = + strat.applyOnRhs[List[A], Unit](runHalf(rightSize, _), rightL) + strat.map2(lres, rres)(unitFn) + } else { + // avoid pattern matching when we know that there is only one element + val a = fa.head + strat.applyToRhs(toGUnit, a) + } - val len = fa.length - if (len == 0) G.unit - else runHalf(len, fa).value - } + val len = fa.length + if (len == 0) G.unit + else strat.rhsToF(runHalf(len, fa)) } def functor: Functor[List] = this @@ -186,21 +194,33 @@ trait ListInstances extends cats.kernel.instances.ListInstances { def align[A, B](fa: List[A], fb: List[B]): List[A Ior B] = alignWith(fa, fb)(identity) - override def alignWith[A, B, C](fa: List[A], fb: List[B])(f: Ior[A, B] => C): List[C] = { - @tailrec def loop(buf: ListBuffer[C], as: List[A], bs: List[B]): List[C] = + override def alignWith[A, B, C](fa: List[A], fb: List[B])( + f: Ior[A, B] => C + ): List[C] = { + @tailrec def loop( + buf: ListBuffer[C], + as: List[A], + bs: List[B] + ): List[C] = (as, bs) match { - case (a :: atail, b :: btail) => loop(buf += f(Ior.Both(a, b)), atail, btail) - case (Nil, Nil) => buf.toList - case (arest, Nil) => (buf ++= arest.map(a => f(Ior.left(a)))).toList - case (Nil, brest) => (buf ++= brest.map(b => f(Ior.right(b)))).toList + case (a :: atail, b :: btail) => + loop(buf += f(Ior.Both(a, b)), atail, btail) + case (Nil, Nil) => buf.toList + case (arest, Nil) => (buf ++= arest.map(a => f(Ior.left(a)))).toList + case (Nil, brest) => + (buf ++= brest.map(b => f(Ior.right(b)))).toList } loop(ListBuffer.empty[C], fa, fb) } - override def mapAccumulate[S, A, B](init: S, fa: List[A])(f: (S, A) => (S, B)): (S, List[B]) = + override def mapAccumulate[S, A, B](init: S, fa: List[A])( + f: (S, A) => (S, B) + ): (S, List[B]) = StaticMethods.mapAccumulateFromStrictFunctor(init, fa, f)(this) - override def mapWithLongIndex[A, B](fa: List[A])(f: (A, Long) => B): List[B] = + override def mapWithLongIndex[A, B](fa: List[A])( + f: (A, Long) => B + ): List[B] = StaticMethods.mapWithLongIndexFromStrictFunctor(fa, f)(this) override def mapWithIndex[A, B](fa: List[A])(f: (A, Int) => B): List[B] = @@ -214,7 +234,9 @@ trait ListInstances extends cats.kernel.instances.ListInstances { override def partitionEither[A, B, C]( fa: List[A] - )(f: A => Either[B, C])(implicit A: Alternative[List]): (List[B], List[C]) = + )( + f: A => Either[B, C] + )(implicit A: Alternative[List]): (List[B], List[C]) = fa.foldRight((List.empty[B], List.empty[C]))((a, acc) => f(a) match { case Left(b) => (b :: acc._1, acc._2) @@ -240,7 +262,9 @@ trait ListInstances extends cats.kernel.instances.ListInstances { override def isEmpty[A](fa: List[A]): Boolean = fa.isEmpty - override def foldM[G[_], A, B](fa: List[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = { + override def foldM[G[_], A, B](fa: List[A], z: B)( + f: (B, A) => G[B] + )(implicit G: Monad[G]): G[B] = { def step(in: (List[A], B)): G[Either[(List[A], B), B]] = in match { case (Nil, b) => G.pure(Right(b)) @@ -253,7 +277,8 @@ trait ListInstances extends cats.kernel.instances.ListInstances { G.tailRecM((fa, z))(step) } - override def fold[A](fa: List[A])(implicit A: Monoid[A]): A = A.combineAll(fa) + override def fold[A](fa: List[A])(implicit A: Monoid[A]): A = + A.combineAll(fa) override def toList[A](fa: List[A]): List[A] = fa @@ -264,17 +289,24 @@ trait ListInstances extends cats.kernel.instances.ListInstances { override def find[A](fa: List[A])(f: A => Boolean): Option[A] = fa.find(f) - override def filter_[A](fa: List[A])(p: A => Boolean): List[A] = fa.filter(p) + override def filter_[A](fa: List[A])(p: A => Boolean): List[A] = + fa.filter(p) - override def takeWhile_[A](fa: List[A])(p: A => Boolean): List[A] = fa.takeWhile(p) + override def takeWhile_[A](fa: List[A])(p: A => Boolean): List[A] = + fa.takeWhile(p) - override def dropWhile_[A](fa: List[A])(p: A => Boolean): List[A] = fa.dropWhile(p) + override def dropWhile_[A](fa: List[A])(p: A => Boolean): List[A] = + fa.dropWhile(p) override def algebra[A]: Monoid[List[A]] = kernel.instances.ListMonoid[A] - override def collectFirst[A, B](fa: List[A])(pf: PartialFunction[A, B]): Option[B] = fa.collectFirst(pf) + override def collectFirst[A, B](fa: List[A])( + pf: PartialFunction[A, B] + ): Option[B] = fa.collectFirst(pf) - override def collectFirstSome[A, B](fa: List[A])(f: A => Option[B]): Option[B] = + override def collectFirstSome[A, B](fa: List[A])( + f: A => Option[B] + ): Option[B] = fa.collectFirst(Function.unlift(f)) override def unit: List[Unit] = _unit @@ -306,43 +338,56 @@ trait ListInstances extends cats.kernel.instances.ListInstances { new (ZipList ~> List) { def apply[A](a: ZipList[A]): List[A] = a.value } def parallel: List ~> ZipList = - new (List ~> ZipList) { def apply[A](v: List[A]): ZipList[A] = new ZipList(v) } + new (List ~> ZipList) { + def apply[A](v: List[A]): ZipList[A] = new ZipList(v) + } } } @suppressUnusedImportWarningForScalaVersionSpecific private[instances] trait ListInstancesBinCompat0 { - implicit val catsStdTraverseFilterForList: TraverseFilter[List] = new TraverseFilter[List] { - val traverse: Traverse[List] = cats.instances.list.catsStdInstancesForList + implicit val catsStdTraverseFilterForList: TraverseFilter[List] = + new TraverseFilter[List] { + val traverse: Traverse[List] = cats.instances.list.catsStdInstancesForList - override def mapFilter[A, B](fa: List[A])(f: (A) => Option[B]): List[B] = fa.collect(Function.unlift(f)) + override def mapFilter[A, B](fa: List[A])(f: (A) => Option[B]): List[B] = + fa.collect(Function.unlift(f)) - override def filter[A](fa: List[A])(f: (A) => Boolean): List[A] = fa.filter(f) + override def filter[A](fa: List[A])(f: (A) => Boolean): List[A] = + fa.filter(f) - override def filterNot[A](fa: List[A])(f: A => Boolean): List[A] = fa.filterNot(f) + override def filterNot[A](fa: List[A])(f: A => Boolean): List[A] = + fa.filterNot(f) - override def collect[A, B](fa: List[A])(f: PartialFunction[A, B]): List[B] = fa.collect(f) + override def collect[A, B](fa: List[A])( + f: PartialFunction[A, B] + ): List[B] = fa.collect(f) - override def flattenOption[A](fa: List[Option[A]]): List[A] = fa.flatten + override def flattenOption[A](fa: List[Option[A]]): List[A] = fa.flatten - def traverseFilter[G[_], A, B](fa: List[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[List[B]] = - if (fa.isEmpty) G.pure(Nil) - else - G match { - case x: StackSafeMonad[G] => x.map(TraverseFilter.traverseFilterDirectly(fa)(f)(x))(_.toList) - case _ => - G.map(Chain.traverseFilterViaChain { - val as = collection.mutable.ArrayBuffer[A]() - as ++= fa - wrapMutableIndexedSeq(as) - }(f))(_.toList) - } + def traverseFilter[G[_], A, B]( + fa: List[A] + )(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[List[B]] = + if (fa.isEmpty) G.pure(Nil) + else + G match { + case x: StackSafeMonad[G] => + x.map(TraverseFilter.traverseFilterDirectly(fa)(f)(x))(_.toList) + case _ => + G.map(Chain.traverseFilterViaChain { + val as = collection.mutable.ArrayBuffer[A]() + as ++= fa + wrapMutableIndexedSeq(as) + }(f))(_.toList) + } - override def filterA[G[_], A](fa: List[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[List[A]] = - traverse - .foldRight(fa, Eval.now(G.pure(List.empty[A])))((x, xse) => - G.map2Eval(f(x), xse)((b, list) => if (b) x :: list else list) - ) - .value - } + override def filterA[G[_], A]( + fa: List[A] + )(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[List[A]] = + traverse + .foldRight(fa, Eval.now(G.pure(List.empty[A])))((x, xse) => + G.map2Eval(f(x), xse)((b, list) => if (b) x :: list else list) + ) + .value + } } diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index 6da57553af..e9f1dc289a 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -30,8 +30,9 @@ import scala.annotation.tailrec import scala.collection.immutable.VectorBuilder trait VectorInstances extends cats.kernel.instances.VectorInstances { - implicit val catsStdInstancesForVector - : Traverse[Vector] & Monad[Vector] & Alternative[Vector] & CoflatMap[Vector] & Align[Vector] = + implicit val catsStdInstancesForVector: Traverse[Vector] & Monad[ + Vector + ] & Alternative[Vector] & CoflatMap[Vector] & Align[Vector] = new FlatMap.AbstractFoldableFlatMap[Vector] with Traverse[Vector] with Monad[Vector] @@ -43,9 +44,12 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { def combineK[A](x: Vector[A], y: Vector[A]): Vector[A] = x ++ y - override def combineAllOptionK[A](as: IterableOnce[Vector[A]]): Option[Vector[A]] = { + override def combineAllOptionK[A]( + as: IterableOnce[Vector[A]] + ): Option[Vector[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(appendAll(iter, Vector.newBuilder[A]).result()) + if (iter.isEmpty) None + else Some(appendAll(iter, Vector.newBuilder[A]).result()) } override def fromIterableOnce[A](as: IterableOnce[A]): Vector[A] = @@ -63,13 +67,19 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { def flatMap[A, B](fa: Vector[A])(f: A => Vector[B]): Vector[B] = fa.flatMap(f) - override def map2[A, B, Z](fa: Vector[A], fb: Vector[B])(f: (A, B) => Z): Vector[Z] = + override def map2[A, B, Z](fa: Vector[A], fb: Vector[B])( + f: (A, B) => Z + ): Vector[Z] = if (fb.isEmpty) Vector.empty // do O(1) work if either is empty - else fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty + else + fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty - private[this] val evalEmpty: Eval[Vector[Nothing]] = Eval.now(Vector.empty) + private[this] val evalEmpty: Eval[Vector[Nothing]] = + Eval.now(Vector.empty) - override def map2Eval[A, B, Z](fa: Vector[A], fb: Eval[Vector[B]])(f: (A, B) => Z): Eval[Vector[Z]] = + override def map2Eval[A, B, Z](fa: Vector[A], fb: Eval[Vector[B]])( + f: (A, B) => Z + ): Eval[Vector[Z]] = if (fa.isEmpty) evalEmpty // no need to evaluate fb else fb.map(fb => map2(fa, fb)(f)) @@ -85,13 +95,17 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { def foldLeft[A, B](fa: Vector[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) - def foldRight[A, B](fa: Vector[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + def foldRight[A, B](fa: Vector[A], lb: Eval[B])( + f: (A, Eval[B]) => Eval[B] + ): Eval[B] = { def loop(i: Int): Eval[B] = if (i < fa.length) f(fa(i), Eval.defer(loop(i + 1))) else lb Eval.defer(loop(0)) } - override def foldMap[A, B](fa: Vector[A])(f: A => B)(implicit B: Monoid[B]): B = + override def foldMap[A, B](fa: Vector[A])(f: A => B)(implicit + B: Monoid[B] + ): B = B.combineAll(fa.iterator.map(f)) def tailRecM[A, B](a: A)(fn: A => Vector[Either[A, B]]): Vector[B] = { @@ -121,76 +135,81 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { override def size[A](fa: Vector[A]): Long = fa.size.toLong override def get[A](fa: Vector[A])(idx: Long): Option[A] = - if (idx < Int.MaxValue && fa.size > idx && idx >= 0) Some(fa(idx.toInt)) else None + if (idx < Int.MaxValue && fa.size > idx && idx >= 0) Some(fa(idx.toInt)) + else None - override def foldMapK[G[_], A, B](fa: Vector[A])(f: A => G[B])(implicit G: MonoidK[G]): G[B] = { + override def foldMapK[G[_], A, B]( + fa: Vector[A] + )(f: A => G[B])(implicit G: MonoidK[G]): G[B] = { def loop(i: Int): Eval[G[B]] = - if (i < fa.length) G.combineKEval(f(fa(i)), Eval.defer(loop(i + 1))) else Eval.now(G.empty) + if (i < fa.length) G.combineKEval(f(fa(i)), Eval.defer(loop(i + 1))) + else Eval.now(G.empty) loop(0).value } - final override def traverse[G[_], A, B](fa: Vector[A])(f: A => G[B])(implicit G: Applicative[G]): G[Vector[B]] = + final override def traverse[G[_], A, B]( + fa: Vector[A] + )(f: A => G[B])(implicit G: Applicative[G]): G[Vector[B]] = G match { - case x: StackSafeMonad[G] => x.map(Traverse.traverseDirectly(fa)(f)(x))(_.toVector) - case _ => G.map(Chain.traverseViaChain(fa)(f))(_.toVector) + case x: StackSafeMonad[G] => + x.map(Traverse.traverseDirectly(fa)(f)(x))(_.toVector) + case _ => G.map(Chain.traverseViaChain(fa)(f))(_.toVector) } - final override def updated_[A, B >: A](fa: Vector[A], idx: Long, b: B): Option[Vector[B]] = + final override def updated_[A, B >: A]( + fa: Vector[A], + idx: Long, + b: B + ): Option[Vector[B]] = if (idx >= 0L && idx < fa.size.toLong) { Some(fa.updated(idx.toInt, b)) } else { None } - /** - * This avoids making a very deep stack by building a tree instead - */ - override def traverseVoid[G[_], A, B](fa: Vector[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = { - G match { - case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa)(f)(x) - case _ => - // the cost of this is O(size) - // c(n) = 1 + 2 * c(n/2) - // invariant: size >= 1 - def runHalf(size: Int, idx: Int): Eval[G[Unit]] = - if (size > 1) { - val leftSize = size / 2 - val rightSize = size - leftSize - runHalf(leftSize, idx) - .flatMap { left => - val right = runHalf(rightSize, idx + leftSize) - G.map2Eval(left, right) { (_, _) => () } - } - } else { - val a = fa(idx) - // we evaluate this at most one time, - // always is a bit cheaper in such cases - // - // Here is the point of the laziness using Eval: - // we avoid calling f(a) or G.void in the - // event that the computation has already - // failed. We do not use laziness to avoid - // traversing fa, which we will do fully - // in all cases. - Eval.always { - val gb = f(a) - G.void(gb) - } - } + /** This avoids making a very deep stack by building a tree instead + */ + override def traverseVoid[G[_], A, B]( + fa: Vector[A] + )(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = { + val unitFn: (Unit, Unit) => Unit = (_, _) => () + val toGUnit: A => G[Unit] = { a => G.void(f(a)) } + val strat = G.traverseStrategy + + // the cost of this is O(size) + // c(n) = 1 + 2 * c(n/2) + // invariant: size >= 1 + def runHalf(size: Int, idx: Int): strat.Rhs[Unit] = + if (size > 1) { + val leftSize = size / 2 + val rightSize = size - leftSize + val lres = strat.applyOnRhs[Int, Unit](runHalf(leftSize, _), idx) + val rres = + strat.applyOnRhs[Int, Unit](runHalf(rightSize, _), idx + leftSize) + strat.map2(lres, rres)(unitFn) + } else { + val a = fa(idx) + strat.applyToRhs(toGUnit, a) + } - val len = fa.length - if (len == 0) G.unit - else runHalf(len, 0).value - } + val len = fa.length + if (len == 0) G.unit + else strat.rhsToF(runHalf(len, 0)) } - override def mapAccumulate[S, A, B](init: S, fa: Vector[A])(f: (S, A) => (S, B)): (S, Vector[B]) = + override def mapAccumulate[S, A, B](init: S, fa: Vector[A])( + f: (S, A) => (S, B) + ): (S, Vector[B]) = StaticMethods.mapAccumulateFromStrictFunctor(init, fa, f)(this) - override def mapWithIndex[A, B](fa: Vector[A])(f: (A, Int) => B): Vector[B] = + override def mapWithIndex[A, B](fa: Vector[A])( + f: (A, Int) => B + ): Vector[B] = StaticMethods.mapWithIndexFromStrictFunctor(fa, f)(this) - override def mapWithLongIndex[A, B](fa: Vector[A])(f: (A, Long) => B): Vector[B] = + override def mapWithLongIndex[A, B](fa: Vector[A])( + f: (A, Long) => B + ): Vector[B] = StaticMethods.mapWithLongIndexFromStrictFunctor(fa, f)(this) override def zipWithIndex[A](fa: Vector[A]): Vector[(A, Int)] = @@ -202,7 +221,9 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { override def isEmpty[A](fa: Vector[A]): Boolean = fa.isEmpty - override def foldM[G[_], A, B](fa: Vector[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = { + override def foldM[G[_], A, B](fa: Vector[A], z: B)( + f: (B, A) => G[B] + )(implicit G: Monad[G]): G[B] = { val length = fa.length G.tailRecM((z, 0)) { case (b, i) => if (i < length) G.map(f(b, fa(i)))(b => Left((b, i + 1))) @@ -210,18 +231,23 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { } } - override def fold[A](fa: Vector[A])(implicit A: Monoid[A]): A = A.combineAll(fa) + override def fold[A](fa: Vector[A])(implicit A: Monoid[A]): A = + A.combineAll(fa) override def toList[A](fa: Vector[A]): List[A] = fa.toList override def toIterable[A](fa: Vector[A]): Iterable[A] = fa - override def reduceLeftOption[A](fa: Vector[A])(f: (A, A) => A): Option[A] = + override def reduceLeftOption[A](fa: Vector[A])( + f: (A, A) => A + ): Option[A] = fa.reduceLeftOption(f) - override def find[A](fa: Vector[A])(f: A => Boolean): Option[A] = fa.find(f) + override def find[A](fa: Vector[A])(f: A => Boolean): Option[A] = + fa.find(f) - override def algebra[A]: Monoid[Vector[A]] = kernel.instances.VectorMonoid[A] + override def algebra[A]: Monoid[Vector[A]] = + kernel.instances.VectorMonoid[A] override def unzip[A, B](fab: Vector[(A, B)]): (Vector[A], Vector[B]) = fab.unzip @@ -231,15 +257,21 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { def align[A, B](fa: Vector[A], fb: Vector[B]): Vector[A Ior B] = { val aLarger = fa.size >= fb.size if (aLarger) { - cats.compat.Vector.zipWith(fa, fb)(Ior.both) ++ fa.drop(fb.size).map(Ior.left) + cats.compat.Vector + .zipWith(fa, fb)(Ior.both) ++ fa.drop(fb.size).map(Ior.left) } else { - cats.compat.Vector.zipWith(fa, fb)(Ior.both) ++ fb.drop(fa.size).map(Ior.right) + cats.compat.Vector + .zipWith(fa, fb)(Ior.both) ++ fb.drop(fa.size).map(Ior.right) } } - override def collectFirst[A, B](fa: Vector[A])(pf: PartialFunction[A, B]): Option[B] = fa.collectFirst(pf) + override def collectFirst[A, B](fa: Vector[A])( + pf: PartialFunction[A, B] + ): Option[B] = fa.collectFirst(pf) - override def collectFirstSome[A, B](fa: Vector[A])(f: A => Option[B]): Option[B] = + override def collectFirstSome[A, B](fa: Vector[A])( + f: A => Option[B] + ): Option[B] = fa.collectFirst(Function.unlift(f)) } @@ -250,45 +282,65 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { new NonEmptyParallel[Vector] { type F[x] = ZipVector[x] - def flatMap: FlatMap[Vector] = cats.instances.vector.catsStdInstancesForVector - def apply: Apply[ZipVector] = ZipVector.catsDataCommutativeApplyForZipVector + def flatMap: FlatMap[Vector] = + cats.instances.vector.catsStdInstancesForVector + def apply: Apply[ZipVector] = + ZipVector.catsDataCommutativeApplyForZipVector def sequential: ZipVector ~> Vector = - new (ZipVector ~> Vector) { def apply[A](a: ZipVector[A]): Vector[A] = a.value } + new (ZipVector ~> Vector) { + def apply[A](a: ZipVector[A]): Vector[A] = a.value + } def parallel: Vector ~> ZipVector = - new (Vector ~> ZipVector) { def apply[A](v: Vector[A]): ZipVector[A] = new ZipVector(v) } + new (Vector ~> ZipVector) { + def apply[A](v: Vector[A]): ZipVector[A] = new ZipVector(v) + } } } @suppressUnusedImportWarningForScalaVersionSpecific private[instances] trait VectorInstancesBinCompat0 { - implicit val catsStdTraverseFilterForVector: TraverseFilter[Vector] = new TraverseFilter[Vector] { - val traverse: Traverse[Vector] = cats.instances.vector.catsStdInstancesForVector + implicit val catsStdTraverseFilterForVector: TraverseFilter[Vector] = + new TraverseFilter[Vector] { + val traverse: Traverse[Vector] = + cats.instances.vector.catsStdInstancesForVector - override def mapFilter[A, B](fa: Vector[A])(f: (A) => Option[B]): Vector[B] = - fa.collect(Function.unlift(f)) + override def mapFilter[A, B](fa: Vector[A])( + f: (A) => Option[B] + ): Vector[B] = + fa.collect(Function.unlift(f)) - override def filter[A](fa: Vector[A])(f: (A) => Boolean): Vector[A] = fa.filter(f) + override def filter[A](fa: Vector[A])(f: (A) => Boolean): Vector[A] = + fa.filter(f) - override def filterNot[A](fa: Vector[A])(f: A => Boolean): Vector[A] = fa.filterNot(f) + override def filterNot[A](fa: Vector[A])(f: A => Boolean): Vector[A] = + fa.filterNot(f) - override def collect[A, B](fa: Vector[A])(f: PartialFunction[A, B]): Vector[B] = fa.collect(f) + override def collect[A, B](fa: Vector[A])( + f: PartialFunction[A, B] + ): Vector[B] = fa.collect(f) - override def flattenOption[A](fa: Vector[Option[A]]): Vector[A] = fa.flatten + override def flattenOption[A](fa: Vector[Option[A]]): Vector[A] = + fa.flatten - def traverseFilter[G[_], A, B](fa: Vector[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[Vector[B]] = - G match { - case x: StackSafeMonad[G] => x.map(TraverseFilter.traverseFilterDirectly(fa)(f)(x))(_.toVector) - case _ => - G.map(Chain.traverseFilterViaChain(fa)(f))(_.toVector) - } + def traverseFilter[G[_], A, B]( + fa: Vector[A] + )(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[Vector[B]] = + G match { + case x: StackSafeMonad[G] => + x.map(TraverseFilter.traverseFilterDirectly(fa)(f)(x))(_.toVector) + case _ => + G.map(Chain.traverseFilterViaChain(fa)(f))(_.toVector) + } - override def filterA[G[_], A](fa: Vector[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[Vector[A]] = - traverse - .foldRight(fa, Eval.now(G.pure(Vector.empty[A])))((x, xse) => - G.map2Eval(f(x), xse)((b, vector) => if (b) x +: vector else vector) - ) - .value - } + override def filterA[G[_], A]( + fa: Vector[A] + )(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[Vector[A]] = + traverse + .foldRight(fa, Eval.now(G.pure(Vector.empty[A])))((x, xse) => + G.map2Eval(f(x), xse)((b, vector) => if (b) x +: vector else vector) + ) + .value + } } diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index a9579bd56a..91a5865bed 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -155,6 +155,8 @@ package object cats { override def get[A](fa: Id[A])(idx: Long): Option[A] = if (idx == 0L) Some(fa) else None override def isEmpty[A](fa: Id[A]): Boolean = false + + override val traverseStrategy: Apply.TraverseStrategy[Id] = Apply.TraverseStrategy.direct(this) } /**