From 5452d019a015f99ef77905c8e68bab014bf44f9f Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Mon, 27 Mar 2017 14:36:48 +0200 Subject: [PATCH 01/28] Add Traverse1 typeclass (#1577) --- core/src/main/scala/cats/package.scala | 6 ++++-- core/src/main/scala/cats/syntax/all.scala | 1 + docs/src/main/tut/typeclasses/typeclasses.md | 2 ++ tests/src/test/scala/cats/tests/SyntaxTests.scala | 13 +++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index 509536c8a6..c44f787d50 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -32,8 +32,8 @@ package object cats { * encodes pure unary function application. */ type Id[A] = A - implicit val catsInstancesForId: Bimonad[Id] with Monad[Id] with Traverse[Id] with Reducible[Id] = - new Bimonad[Id] with Monad[Id] with Traverse[Id] with Reducible[Id] { + implicit val catsInstancesForId: Bimonad[Id] with Monad[Id] with Traverse1[Id] = + new Bimonad[Id] with Monad[Id] with Traverse1[Id] { def pure[A](a: A): A = a def extract[A](a: A): A = a def flatMap[A, B](a: A)(f: A => B): B = f(a) @@ -53,6 +53,8 @@ package object cats { f(a, lb) def traverse[G[_], A, B](a: A)(f: A => G[B])(implicit G: Applicative[G]): G[B] = f(a) + def traverse1[G[_], A, B](a: A)(f: A => G[B])(implicit G: Apply[G]): G[B] = + f(a) override def foldMap[A, B](fa: Id[A])(f: A => B)(implicit B: Monoid[B]): B = f(fa) override def reduce[A](fa: Id[A])(implicit A: Semigroup[A]): A = fa diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index 00197e881f..3612530314 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -40,6 +40,7 @@ trait AllSyntax with TransLiftSyntax with TraverseFilterSyntax with TraverseSyntax + with Traverse1Syntax with TupleSyntax with ValidatedSyntax with VectorSyntax diff --git a/docs/src/main/tut/typeclasses/typeclasses.md b/docs/src/main/tut/typeclasses/typeclasses.md index ab75a14b46..aeed54a854 100644 --- a/docs/src/main/tut/typeclasses/typeclasses.md +++ b/docs/src/main/tut/typeclasses/typeclasses.md @@ -267,6 +267,8 @@ type class instances easy. Foldable -> Traverse Functor -> Traverse Foldable -> Reducible + Reducible -> Traverse1 + Traverse -> Traverse1 } ) diff --git a/tests/src/test/scala/cats/tests/SyntaxTests.scala b/tests/src/test/scala/cats/tests/SyntaxTests.scala index 23172cd7bc..5470931000 100644 --- a/tests/src/test/scala/cats/tests/SyntaxTests.scala +++ b/tests/src/test/scala/cats/tests/SyntaxTests.scala @@ -139,6 +139,19 @@ object SyntaxTests extends AllInstances with AllSyntax { val gunit: G[F[A]] = fga.sequence } + + def testTraverse1[F[_]: Traverse1: FlatMap, G[_]: Apply: SemigroupK, A: Semigroup, B, Z]: Unit = { + val fa = mock[F[A]] + val f1 = mock[A => G[B]] + val gfb: G[F[B]] = fa.traverse1(f1) + + val f2 = mock[A => G[F[B]]] + val gfb2: G[F[B]] = fa.flatTraverse1(f2) + + val fga = mock[F[G[A]]] + val gunit: G[F[A]] = fga.sequence1 + } + def testReducible[F[_]: Reducible, G[_]: Apply: SemigroupK, A: Semigroup, B, Z]: Unit = { val fa = mock[F[A]] val f1 = mock[(A, A) => A] From 96d79d28cd2aac670937e7f9813135fc82160a7d Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Tue, 18 Apr 2017 09:53:10 -0400 Subject: [PATCH 02/28] Add tests and laws for Traverse1 typeclass (#1577) --- core/src/main/scala/cats/Traverse1.scala | 133 ++++++++++++++++++ .../main/scala/cats/data/NonEmptyList.scala | 13 +- core/src/main/scala/cats/data/OneAnd.scala | 24 +++- core/src/main/scala/cats/package.scala | 2 - core/src/main/scala/cats/syntax/package.scala | 1 + .../main/scala/cats/syntax/reducible.scala | 9 +- .../main/scala/cats/syntax/traverse1.scala | 19 +++ .../main/scala/cats/laws/Traverse1Laws.scala | 73 ++++++++++ .../cats/laws/discipline/Traverse1Tests.scala | 59 ++++++++ 9 files changed, 316 insertions(+), 17 deletions(-) create mode 100644 core/src/main/scala/cats/Traverse1.scala create mode 100644 core/src/main/scala/cats/syntax/traverse1.scala create mode 100644 laws/src/main/scala/cats/laws/Traverse1Laws.scala create mode 100644 laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala new file mode 100644 index 0000000000..71cef07590 --- /dev/null +++ b/core/src/main/scala/cats/Traverse1.scala @@ -0,0 +1,133 @@ +package cats + +import cats.data.NonEmptyList +import simulacrum.typeclass + +@typeclass trait Traverse1[F[_]] extends Traverse[F] with Reducible[F] { + + def traverse1[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]] + + def sequence1[G[_]: Apply, A](fga: F[G[A]]): G[F[A]] = + traverse1(fga)(identity) + + def flatTraverse1[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Apply[G], F: FlatMap[F]): G[F[B]] = + G.map(traverse1(fa)(f))(F.flatten) + + def flatSequence[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] = + G.map(traverse1(fgfa)(identity))(F.flatten) + + + override def traverse[G[_] : Applicative, A, B](fa: F[A])(f: (A) => G[B]): G[F[B]] = + traverse1(fa)(f) + + override def sequence[G[_] : Applicative, A](fga: F[G[A]]): G[F[A]] = + traverse1(fga)(identity) + + + + def traverse1U[A, GB](fa: F[A])(f: A => GB)(implicit G: Unapply[Apply, GB]): G.M[F[G.A]] = + traverse1(fa)(G.subst.compose(f))(G.TC) + + def sequenceU1[GA](fga: F[GA])(implicit U: Unapply[Apply, GA]): U.M[F[U.A]] = + traverse1(fga)(U.subst)(U.TC) + + override def reduceMap[A, B](fa: F[A])(f: (A) => B)(implicit B: Semigroup[B]): B = + reduceLeft(traverse1[Id, A, B](fa)(f))(B.combine) + + override def map[A, B](fa: F[A])(f: A => B): F[B] = + traverse1[Id, A, B](fa)(f) + +} + +/** + * This class defines a `Reducible[F]` in terms of a `Foldable[G]` + * together with a `split` method, `F[A]` => `(A, G[A])`. + * + * This class can be used on any type where the first value (`A`) and + * the "rest" of the values (`G[A]`) can be easily found. + */ +abstract class NonEmptyTraverse1[F[_], G[_]](implicit G: Foldable[G] with Traverse[G]) extends Traverse1[F] { + def split[A](fa: F[A]): (A, G[A]) + + def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = { + val (a, ga) = split(fa) + G.foldLeft(ga, f(b, a))(f) + } + + def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + Always(split(fa)).flatMap { case (a, ga) => + f(a, G.foldRight(ga, lb)(f)) + } + + def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = { + val (a, ga) = split(fa) + G.foldLeft(ga, f(a))((b, a) => g(b, a)) + } + + def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = + Always(split(fa)).flatMap { case (a, ga) => + G.reduceRightToOption(ga)(f)(g).flatMap { + case Some(b) => g(a, Now(b)) + case None => Later(f(a)) + } + } + + override def size[A](fa: F[A]): Long = { + val (_, tail) = split(fa) + 1 + G.size(tail) + } + + override def fold[A](fa: F[A])(implicit A: Monoid[A]): A = { + val (a, ga) = split(fa) + A.combine(a, G.fold(ga)) + } + + override def foldM[H[_], A, B](fa: F[A], z: B)(f: (B, A) => H[B])(implicit H: Monad[H]): H[B] = { + val (a, ga) = split(fa) + H.flatMap(f(z, a))(G.foldM(ga, _)(f)) + } + + override def find[A](fa: F[A])(f: A => Boolean): Option[A] = { + val (a, ga) = split(fa) + if (f(a)) Some(a) else G.find(ga)(f) + } + + override def exists[A](fa: F[A])(p: A => Boolean): Boolean = { + val (a, ga) = split(fa) + p(a) || G.exists(ga)(p) + } + + override def forall[A](fa: F[A])(p: A => Boolean): Boolean = { + val (a, ga) = split(fa) + p(a) && G.forall(ga)(p) + } + + override def toList[A](fa: F[A]): List[A] = { + val (a, ga) = split(fa) + a :: G.toList(ga) + } + + override def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] = { + val (a, ga) = split(fa) + NonEmptyList(a, G.toList(ga)) + } + + override def filter_[A](fa: F[A])(p: A => Boolean): List[A] = { + val (a, ga) = split(fa) + val filteredTail = G.filter_(ga)(p) + if (p(a)) a :: filteredTail else filteredTail + } + + override def takeWhile_[A](fa: F[A])(p: A => Boolean): List[A] = { + val (a, ga) = split(fa) + if (p(a)) a :: G.takeWhile_(ga)(p) else Nil + } + + override def dropWhile_[A](fa: F[A])(p: A => Boolean): List[A] = { + val (a, ga) = split(fa) + if (p(a)) G.dropWhile_(ga)(p) else a :: G.toList(ga) + } + + + +} diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 1031b845cd..5f8e5ca426 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -3,6 +3,8 @@ package data import cats.instances.list._ import cats.syntax.order._ +import cats.syntax.semigroup._ +import cats.syntax.cartesian._ import scala.annotation.tailrec import scala.collection.immutable.TreeSet @@ -233,9 +235,9 @@ object NonEmptyList extends NonEmptyListInstances { private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 { implicit val catsDataInstancesForNonEmptyList: SemigroupK[NonEmptyList] with Reducible[NonEmptyList] - with Comonad[NonEmptyList] with Traverse[NonEmptyList] with Monad[NonEmptyList] = + with Comonad[NonEmptyList] with Traverse1[NonEmptyList] with Monad[NonEmptyList] = new NonEmptyReducible[NonEmptyList, List] with SemigroupK[NonEmptyList] with Comonad[NonEmptyList] - with Traverse[NonEmptyList] with Monad[NonEmptyList] { + with Traverse1[NonEmptyList] with Monad[NonEmptyList] { def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] = a concat b @@ -262,7 +264,12 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 def extract[A](fa: NonEmptyList[A]): A = fa.head - def traverse[G[_], A, B](fa: NonEmptyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] = + def traverse1[G[_] : Apply, A, B](as: NonEmptyList[A])(f: A => G[B]): G[NonEmptyList[B]] = { + as.map(a => Apply[G].map(f(a))(NonEmptyList.of(_))) + .reduceLeft((acc, a) => (acc |@| a).map( _ |+| _ )) + } + + override def traverse[G[_], A, B](fa: NonEmptyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] = fa traverse f override def foldLeft[A, B](fa: NonEmptyList[A], b: B)(f: (B, A) => B): B = diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 9368ed9179..153788990d 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -54,6 +54,10 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) { def forall(p: A => Boolean)(implicit F: Foldable[F]): Boolean = p(head) && F.forall(tail)(p) + + def reduceLeft(f: (A, A) => A)(implicit F: Foldable[F]): A = + F.foldLeft(tail, head)(f) + /** * Left-associative fold on the structure using f. */ @@ -202,10 +206,22 @@ private[data] trait OneAndLowPriority1 extends OneAndLowPriority0 { } private[data] trait OneAndLowPriority2 extends OneAndLowPriority1 { - implicit def catsDataTraverseForOneAnd[F[_]](implicit F: Traverse[F]): Traverse[OneAnd[F, ?]] = - new Traverse[OneAnd[F, ?]] { - def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] = { - G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value + implicit def catsDataTraverseForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = + new Traverse1[OneAnd[F, ?]] { + + override def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { + import cats.syntax.cartesian._ + + fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) + .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) + } + + def reduceLeftTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (B, A) => B): B = { + fa.foldLeft(f(fa.head))(g) + } + + def reduceRightTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.foldRight(Always(f(fa.head)))(g) } def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index c44f787d50..f9e134fc21 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -51,8 +51,6 @@ package object cats { def foldLeft[A, B](a: A, b: B)(f: (B, A) => B) = f(b, a) def foldRight[A, B](a: A, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = f(a, lb) - def traverse[G[_], A, B](a: A)(f: A => G[B])(implicit G: Applicative[G]): G[B] = - f(a) def traverse1[G[_], A, B](a: A)(f: A => G[B])(implicit G: Apply[G]): G[B] = f(a) override def foldMap[A, B](fa: Id[A])(f: A => B)(implicit B: Monoid[B]): B = f(fa) diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index bc9962eac5..baccfd5e57 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -39,6 +39,7 @@ package object syntax { object strong extends StrongSyntax object transLift extends TransLiftSyntax object traverse extends TraverseSyntax + object traverse1 extends Traverse1Syntax object traverseFilter extends TraverseFilterSyntax object tuple extends TupleSyntax object validated extends ValidatedSyntax diff --git a/core/src/main/scala/cats/syntax/reducible.scala b/core/src/main/scala/cats/syntax/reducible.scala index 8d5b89f80c..46f3cacad0 100644 --- a/core/src/main/scala/cats/syntax/reducible.scala +++ b/core/src/main/scala/cats/syntax/reducible.scala @@ -9,11 +9,4 @@ private[syntax] trait ReducibleSyntax1 { } } -trait ReducibleSyntax extends Reducible.ToReducibleOps with ReducibleSyntax1 { - implicit def catsSyntaxNestedReducible[F[_]: Reducible, G[_], A](fga: F[G[A]]): NestedReducibleOps[F, G, A] = - new NestedReducibleOps[F, G, A](fga) -} - -final class NestedReducibleOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Reducible[F]) { - def reduceK(implicit G: SemigroupK[G]): G[A] = F.reduceK(fga) -} +trait ReducibleSyntax extends Reducible.ToReducibleOps with ReducibleSyntax1 \ No newline at end of file diff --git a/core/src/main/scala/cats/syntax/traverse1.scala b/core/src/main/scala/cats/syntax/traverse1.scala new file mode 100644 index 0000000000..0a0d92b478 --- /dev/null +++ b/core/src/main/scala/cats/syntax/traverse1.scala @@ -0,0 +1,19 @@ +package cats +package syntax + +private[syntax] trait Traverse1Syntax1 { + implicit def catsSyntaxUTraverse1[FA](fa: FA)(implicit U: Unapply[Traverse1, FA]): Traverse1.Ops[U.M, U.A] = + new Traverse1.Ops[U.M, U.A] { + val self = U.subst(fa) + val typeClassInstance = U.TC + } +} + +trait Traverse1Syntax extends Traverse1.ToTraverse1Ops with Traverse1Syntax1 { + implicit def catsSyntaxNestedTraverse1[F[_]: Traverse1, G[_], A](fga: F[G[A]]): NestedTraverse1Ops[F, G, A] = + new NestedTraverse1Ops[F, G, A](fga) +} + +final class NestedTraverse1Ops[F[_], G[_], A](fga: F[G[A]])(implicit F: Traverse1[F]) { + def reduceK(implicit G: SemigroupK[G]): G[A] = F.reduceK(fga) +} diff --git a/laws/src/main/scala/cats/laws/Traverse1Laws.scala b/laws/src/main/scala/cats/laws/Traverse1Laws.scala new file mode 100644 index 0000000000..e498289482 --- /dev/null +++ b/laws/src/main/scala/cats/laws/Traverse1Laws.scala @@ -0,0 +1,73 @@ +package cats.laws + + +import cats.{Apply, Id, Semigroup, Traverse1} +import cats.data.{Const, Nested} +import cats.syntax.traverse1._ +import cats.syntax.reducible._ + +trait Traverse1Laws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] { + implicit override def F: Traverse1[F] + + def traverse1Identity[A, B](fa: F[A], f: A => B): IsEq[F[B]] = { + fa.traverse1[Id, B](f) <-> F.map(fa)(f) + } + + def traverse1SequentialComposition[A, B, C, M[_], N[_]]( + fa: F[A], + f: A => M[B], + g: B => N[C] + )(implicit + N: Apply[N], + M: Apply[M] + ): IsEq[Nested[M, N, F[C]]] = { + + val lhs = Nested(M.map(fa.traverse1(f))(fb => fb.traverse1(g))) + val rhs = fa.traverse1[Nested[M, N, ?], C](a => Nested(M.map(f(a))(g))) + lhs <-> rhs + } + + def traverse1ParallelComposition[A, B, M[_], N[_]]( + fa: F[A], + f: A => M[B], + g: A => N[B] + )(implicit + N: Apply[N], + M: Apply[M] + ): IsEq[(M[F[B]], N[F[B]])] = { + type MN[Z] = (M[Z], N[Z]) + implicit val MN = new Apply[MN] { + def ap[X, Y](f: MN[X => Y])(fa: MN[X]): MN[Y] = { + val (fam, fan) = fa + val (fm, fn) = f + (M.ap(fm)(fam), N.ap(fn)(fan)) + } + override def map[X, Y](fx: MN[X])(f: X => Y): MN[Y] = { + val (mx, nx) = fx + (M.map(mx)(f), N.map(nx)(f)) + } + override def product[X, Y](fx: MN[X], fy: MN[Y]): MN[(X, Y)] = { + val (mx, nx) = fx + val (my, ny) = fy + (M.product(mx, my), N.product(nx, ny)) + } + } + val lhs: MN[F[B]] = fa.traverse1[MN, B](a => (f(a), g(a))) + val rhs: MN[F[B]] = (fa.traverse1(f), fa.traverse1(g)) + lhs <-> rhs + } + + def reduceMapDerived[A, B]( + fa: F[A], + f: A => B + )(implicit B: Semigroup[B]): IsEq[B] = { + val lhs: B = fa.traverse1[Const[B, ?], B](a => Const(f(a))).getConst + val rhs: B = fa.reduceMap(f) + lhs <-> rhs + } +} + +object Traverse1Laws { + def apply[F[_]](implicit ev: Traverse1[F]): Traverse1Laws[F] = + new Traverse1Laws[F] { def F: Traverse1[F] = ev } +} diff --git a/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala b/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala new file mode 100644 index 0000000000..18065d8c1c --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala @@ -0,0 +1,59 @@ +package cats.laws.discipline + + +import org.scalacheck.{Arbitrary, Cogen, Prop} +import Prop.forAll +import cats.{Applicative, Eq, Monoid, Traverse1} +import cats.laws.Traverse1Laws + + +trait Traverse1Tests[F[_]] extends TraverseTests[F] with ReducibleTests[F] { + def laws: Traverse1Laws[F] + + def traverse1[G[_]: Applicative, A: Arbitrary, B: Arbitrary, C: Arbitrary, M: Arbitrary, X[_]: Applicative, Y[_]: Applicative](implicit + ArbFA: Arbitrary[F[A]], + ArbXB: Arbitrary[X[B]], + ArbYB: Arbitrary[Y[B]], + ArbYC: Arbitrary[Y[C]], + ArbFB: Arbitrary[F[B]], + ArbFGA: Arbitrary[F[G[A]]], + ArbGB: Arbitrary[G[B]], + CogenA: Cogen[A], + CogenB: Cogen[B], + CogenC: Cogen[C], + CogenM: Cogen[M], + M: Monoid[M], + MB: Monoid[B], + EqFA: Eq[F[A]], + EqFC: Eq[F[C]], + EqG: Eq[G[Unit]], + EqM: Eq[M], + EqA: Eq[A], + EqB: Eq[B], + EqXYFC: Eq[X[Y[F[C]]]], + EqXFB: Eq[X[F[B]]], + EqYFB: Eq[Y[F[B]]], + EqOptionA: Eq[Option[A]] + ): RuleSet = { + implicit def EqXFBYFB : Eq[(X[F[B]], Y[F[B]])] = new Eq[(X[F[B]], Y[F[B]])] { + override def eqv(x: (X[F[B]], Y[F[B]]), y: (X[F[B]], Y[F[B]])): Boolean = + EqXFB.eqv(x._1, y._1) && EqYFB.eqv(x._2, y._2) + } + new RuleSet { + def name: String = "traverse1" + def bases: Seq[(String, RuleSet)] = Nil + def parents: Seq[RuleSet] = Seq(traverse[A, B, C, M, X, Y], reducible[G, A, B]) + def props: Seq[(String, Prop)] = Seq( + "traverse1 identity" -> forAll(laws.traverse1Identity[A, C] _), + "traverse1 sequential composition" -> forAll(laws.traverse1SequentialComposition[A, B, C, X, Y] _), + "traverse1 parallel composition" -> forAll(laws.traverse1ParallelComposition[A, B, X, Y] _), + "traverse1 derive reduceMap" -> forAll(laws.reduceMapDerived[A, M] _) + ) + } + } +} + +object Traverse1Tests { + def apply[F[_]: Traverse1]: Traverse1Tests[F] = + new Traverse1Tests[F] { def laws: Traverse1Laws[F] = Traverse1Laws[F] } +} \ No newline at end of file From 3e9afe312200758413f0af41135b96b4e1f022d8 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sat, 22 Apr 2017 09:29:48 -0400 Subject: [PATCH 03/28] Format with newline at end of file --- core/src/main/scala/cats/syntax/reducible.scala | 2 +- laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/syntax/reducible.scala b/core/src/main/scala/cats/syntax/reducible.scala index 46f3cacad0..e977e09735 100644 --- a/core/src/main/scala/cats/syntax/reducible.scala +++ b/core/src/main/scala/cats/syntax/reducible.scala @@ -9,4 +9,4 @@ private[syntax] trait ReducibleSyntax1 { } } -trait ReducibleSyntax extends Reducible.ToReducibleOps with ReducibleSyntax1 \ No newline at end of file +trait ReducibleSyntax extends Reducible.ToReducibleOps with ReducibleSyntax1 diff --git a/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala b/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala index 18065d8c1c..2ad8fe38dd 100644 --- a/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala +++ b/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala @@ -56,4 +56,4 @@ trait Traverse1Tests[F[_]] extends TraverseTests[F] with ReducibleTests[F] { object Traverse1Tests { def apply[F[_]: Traverse1]: Traverse1Tests[F] = new Traverse1Tests[F] { def laws: Traverse1Laws[F] = Traverse1Laws[F] } -} \ No newline at end of file +} From 7ff5e1e13c8f3d22fcd88ffd0d731b6848c68843 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Thu, 18 May 2017 11:36:05 +0200 Subject: [PATCH 04/28] Replace NonEmptyReducible with NonEmptyTraverse1 --- core/src/main/scala/cats/Reducible.scala | 90 ------------------- core/src/main/scala/cats/Traverse1.scala | 2 +- .../main/scala/cats/data/NonEmptyList.scala | 4 +- .../main/scala/cats/data/NonEmptyVector.scala | 12 ++- core/src/main/scala/cats/data/OneAnd.scala | 59 ++++++------ 5 files changed, 36 insertions(+), 131 deletions(-) diff --git a/core/src/main/scala/cats/Reducible.scala b/core/src/main/scala/cats/Reducible.scala index 9f3e26684c..b0c5178c36 100644 --- a/core/src/main/scala/cats/Reducible.scala +++ b/core/src/main/scala/cats/Reducible.scala @@ -187,93 +187,3 @@ import simulacrum.typeclass override def maximumOption[A](fa: F[A])(implicit A: Order[A]): Option[A] = Some(maximum(fa)) } - -/** - * This class defines a `Reducible[F]` in terms of a `Foldable[G]` - * together with a `split` method, `F[A]` => `(A, G[A])`. - * - * This class can be used on any type where the first value (`A`) and - * the "rest" of the values (`G[A]`) can be easily found. - */ -abstract class NonEmptyReducible[F[_], G[_]](implicit G: Foldable[G]) extends Reducible[F] { - def split[A](fa: F[A]): (A, G[A]) - - def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = { - val (a, ga) = split(fa) - G.foldLeft(ga, f(b, a))(f) - } - - def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - Always(split(fa)).flatMap { case (a, ga) => - f(a, G.foldRight(ga, lb)(f)) - } - - def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = { - val (a, ga) = split(fa) - G.foldLeft(ga, f(a))((b, a) => g(b, a)) - } - - def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = - Always(split(fa)).flatMap { case (a, ga) => - G.reduceRightToOption(ga)(f)(g).flatMap { - case Some(b) => g(a, Now(b)) - case None => Later(f(a)) - } - } - - override def size[A](fa: F[A]): Long = { - val (_, tail) = split(fa) - 1 + G.size(tail) - } - - override def fold[A](fa: F[A])(implicit A: Monoid[A]): A = { - val (a, ga) = split(fa) - A.combine(a, G.fold(ga)) - } - - override def foldM[H[_], A, B](fa: F[A], z: B)(f: (B, A) => H[B])(implicit H: Monad[H]): H[B] = { - val (a, ga) = split(fa) - H.flatMap(f(z, a))(G.foldM(ga, _)(f)) - } - - override def find[A](fa: F[A])(f: A => Boolean): Option[A] = { - val (a, ga) = split(fa) - if (f(a)) Some(a) else G.find(ga)(f) - } - - override def exists[A](fa: F[A])(p: A => Boolean): Boolean = { - val (a, ga) = split(fa) - p(a) || G.exists(ga)(p) - } - - override def forall[A](fa: F[A])(p: A => Boolean): Boolean = { - val (a, ga) = split(fa) - p(a) && G.forall(ga)(p) - } - - override def toList[A](fa: F[A]): List[A] = { - val (a, ga) = split(fa) - a :: G.toList(ga) - } - - override def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] = { - val (a, ga) = split(fa) - NonEmptyList(a, G.toList(ga)) - } - - override def filter_[A](fa: F[A])(p: A => Boolean): List[A] = { - val (a, ga) = split(fa) - val filteredTail = G.filter_(ga)(p) - if (p(a)) a :: filteredTail else filteredTail - } - - override def takeWhile_[A](fa: F[A])(p: A => Boolean): List[A] = { - val (a, ga) = split(fa) - if (p(a)) a :: G.takeWhile_(ga)(p) else Nil - } - - override def dropWhile_[A](fa: F[A])(p: A => Boolean): List[A] = { - val (a, ga) = split(fa) - if (p(a)) G.dropWhile_(ga)(p) else a :: G.toList(ga) - } -} diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index 71cef07590..cfed35fbaf 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -40,7 +40,7 @@ import simulacrum.typeclass } /** - * This class defines a `Reducible[F]` in terms of a `Foldable[G]` + * This class defines a `Traverse1[F]` in terms of a `Foldable[G] with Traverse[G]` * together with a `split` method, `F[A]` => `(A, G[A])`. * * This class can be used on any type where the first value (`A`) and diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index ea88d2d6c6..3da0ebdd16 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -326,8 +326,8 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 implicit val catsDataInstancesForNonEmptyList: SemigroupK[NonEmptyList] with Reducible[NonEmptyList] with Comonad[NonEmptyList] with Traverse1[NonEmptyList] with Monad[NonEmptyList] = - new NonEmptyReducible[NonEmptyList, List] with SemigroupK[NonEmptyList] with Comonad[NonEmptyList] - with Traverse1[NonEmptyList] with Monad[NonEmptyList] { + new NonEmptyTraverse1[NonEmptyList, List] with SemigroupK[NonEmptyList] with Comonad[NonEmptyList] + with Monad[NonEmptyList] { def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] = a concat b diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index 73d7e24ea9..03cde48f27 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -4,6 +4,8 @@ package data import scala.annotation.tailrec import scala.collection.immutable.{TreeSet, VectorBuilder} import cats.instances.vector._ +import cats.syntax.cartesian._ +import cats.syntax.semigroup._ /** * A data type which represents a `Vector` guaranteed to contain at least one element. @@ -190,8 +192,8 @@ private[data] sealed trait NonEmptyVectorInstances { implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector] with Reducible[NonEmptyVector] with Comonad[NonEmptyVector] with Traverse[NonEmptyVector] with Monad[NonEmptyVector] = - new NonEmptyReducible[NonEmptyVector, Vector] with SemigroupK[NonEmptyVector] with Comonad[NonEmptyVector] - with Traverse[NonEmptyVector] with Monad[NonEmptyVector] { + new NonEmptyTraverse1[NonEmptyVector, Vector] with SemigroupK[NonEmptyVector] with Comonad[NonEmptyVector] + with Monad[NonEmptyVector] { def combineK[A](a: NonEmptyVector[A], b: NonEmptyVector[A]): NonEmptyVector[A] = a concatNev b @@ -226,8 +228,10 @@ private[data] sealed trait NonEmptyVectorInstances { def extract[A](fa: NonEmptyVector[A]): A = fa.head - def traverse[G[_], A, B](fa: NonEmptyVector[A])(f: (A) => G[B])(implicit G: Applicative[G]): G[NonEmptyVector[B]] = - G.map2Eval(f(fa.head), Always(Traverse[Vector].traverse(fa.tail)(f)))(NonEmptyVector(_, _)).value + def traverse1[G[_] : Apply, A, B](as: NonEmptyVector[A])(f: A => G[B]): G[NonEmptyVector[B]] = { + as.map(a => Apply[G].map(f(a))(NonEmptyVector.of(_))) + .reduceLeft((acc, a) => (acc |@| a).map( _ |+| _ )) + } override def foldLeft[A, B](fa: NonEmptyVector[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 6f8c93b50f..96a7875380 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -98,7 +98,7 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) { s"OneAnd(${A.show(head)}, ${FA.show(tail)})" } -private[data] sealed trait OneAndInstances extends OneAndLowPriority2 { +private[data] sealed trait OneAndInstances { implicit def catsDataEqForOneAnd[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] = new Eq[OneAnd[F, A]]{ @@ -117,11 +117,31 @@ private[data] sealed trait OneAndInstances extends OneAndLowPriority2 { implicit def catsDataSemigroupForOneAnd[F[_]: Alternative, A]: Semigroup[OneAnd[F, A]] = catsDataSemigroupKForOneAnd[F].algebra - implicit def catsDataReducibleForOneAnd[F[_]](implicit F: Foldable[F]): Reducible[OneAnd[F, ?]] = - new NonEmptyReducible[OneAnd[F, ?], F] { - override def split[A](fa: OneAnd[F, A]): (A, F[A]) = (fa.head, fa.tail) + implicit def catsDataTraverseForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = + new Traverse1[OneAnd[F, ?]] { + + override def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { + import cats.syntax.cartesian._ + + fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) + .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) + } + + def reduceLeftTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (B, A) => B): B = { + fa.foldLeft(f(fa.head))(g) + } + + def reduceRightTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.foldRight(Always(f(fa.head)))(g) + } + + def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { + fa.foldLeft(b)(f) + } - override def size[A](fa: OneAnd[F, A]): Long = 1 + F.size(fa.tail) + def foldRight[A, B](fa: OneAnd[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.foldRight(lb)(f) + } } implicit def catsDataMonadForOneAnd[F[_]](implicit monad: MonadCombine[F]): Monad[OneAnd[F, ?]] = @@ -205,33 +225,4 @@ private[data] trait OneAndLowPriority1 extends OneAndLowPriority0 { } -private[data] trait OneAndLowPriority2 extends OneAndLowPriority1 { - implicit def catsDataTraverseForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = - new Traverse1[OneAnd[F, ?]] { - - override def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { - import cats.syntax.cartesian._ - - fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) - .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) - } - - def reduceLeftTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (B, A) => B): B = { - fa.foldLeft(f(fa.head))(g) - } - - def reduceRightTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = { - fa.foldRight(Always(f(fa.head)))(g) - } - - def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { - fa.foldLeft(b)(f) - } - - def foldRight[A, B](fa: OneAnd[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { - fa.foldRight(lb)(f) - } - } -} - object OneAnd extends OneAndInstances From 2e2e7ac6729c3a370f6ed1ce104f1ae2e2e32213 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Thu, 18 May 2017 11:37:22 +0200 Subject: [PATCH 05/28] Add Scaladocs to Traverse1 --- core/src/main/scala/cats/Traverse1.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index cfed35fbaf..2d45264c7d 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -3,6 +3,12 @@ package cats import cats.data.NonEmptyList import simulacrum.typeclass +/** + * Traverse1, also known as Traversable1. + * + * `Traverse1` is like a non-empty `Traverse`. In addition to the traverse and sequence + * methods it provides traverse1 and sequence1 methods which require an `Apply` instance instead of `Applicative`. + */ @typeclass trait Traverse1[F[_]] extends Traverse[F] with Reducible[F] { def traverse1[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]] From 82f45378a67e01cc51a79e63c4726f9c32366cf7 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Thu, 18 May 2017 11:40:46 +0200 Subject: [PATCH 06/28] Remove unneeded Unapply version --- core/src/main/scala/cats/Traverse1.scala | 7 ------- core/src/main/scala/cats/syntax/traverse1.scala | 10 +--------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index 2d45264c7d..f0bfa0bccd 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -30,13 +30,6 @@ import simulacrum.typeclass traverse1(fga)(identity) - - def traverse1U[A, GB](fa: F[A])(f: A => GB)(implicit G: Unapply[Apply, GB]): G.M[F[G.A]] = - traverse1(fa)(G.subst.compose(f))(G.TC) - - def sequenceU1[GA](fga: F[GA])(implicit U: Unapply[Apply, GA]): U.M[F[U.A]] = - traverse1(fga)(U.subst)(U.TC) - override def reduceMap[A, B](fa: F[A])(f: (A) => B)(implicit B: Semigroup[B]): B = reduceLeft(traverse1[Id, A, B](fa)(f))(B.combine) diff --git a/core/src/main/scala/cats/syntax/traverse1.scala b/core/src/main/scala/cats/syntax/traverse1.scala index 0a0d92b478..21b3b5e0dd 100644 --- a/core/src/main/scala/cats/syntax/traverse1.scala +++ b/core/src/main/scala/cats/syntax/traverse1.scala @@ -1,15 +1,7 @@ package cats package syntax -private[syntax] trait Traverse1Syntax1 { - implicit def catsSyntaxUTraverse1[FA](fa: FA)(implicit U: Unapply[Traverse1, FA]): Traverse1.Ops[U.M, U.A] = - new Traverse1.Ops[U.M, U.A] { - val self = U.subst(fa) - val typeClassInstance = U.TC - } -} - -trait Traverse1Syntax extends Traverse1.ToTraverse1Ops with Traverse1Syntax1 { +trait Traverse1Syntax extends Traverse1.ToTraverse1Ops { implicit def catsSyntaxNestedTraverse1[F[_]: Traverse1, G[_], A](fga: F[G[A]]): NestedTraverse1Ops[F, G, A] = new NestedTraverse1Ops[F, G, A](fga) } From ab40d79df4b1327f2ff988adeb0d7ca87b81dd71 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Thu, 18 May 2017 12:44:31 +0200 Subject: [PATCH 07/28] Readd Reducible instance to OneAnd so a Monad is not needed for a Foldable instance --- core/src/main/scala/cats/Reducible.scala | 90 ++++++++++++++++++++++ core/src/main/scala/cats/Traverse1.scala | 2 - core/src/main/scala/cats/data/OneAnd.scala | 58 ++++++++------ 3 files changed, 123 insertions(+), 27 deletions(-) diff --git a/core/src/main/scala/cats/Reducible.scala b/core/src/main/scala/cats/Reducible.scala index b0c5178c36..9f3e26684c 100644 --- a/core/src/main/scala/cats/Reducible.scala +++ b/core/src/main/scala/cats/Reducible.scala @@ -187,3 +187,93 @@ import simulacrum.typeclass override def maximumOption[A](fa: F[A])(implicit A: Order[A]): Option[A] = Some(maximum(fa)) } + +/** + * This class defines a `Reducible[F]` in terms of a `Foldable[G]` + * together with a `split` method, `F[A]` => `(A, G[A])`. + * + * This class can be used on any type where the first value (`A`) and + * the "rest" of the values (`G[A]`) can be easily found. + */ +abstract class NonEmptyReducible[F[_], G[_]](implicit G: Foldable[G]) extends Reducible[F] { + def split[A](fa: F[A]): (A, G[A]) + + def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = { + val (a, ga) = split(fa) + G.foldLeft(ga, f(b, a))(f) + } + + def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + Always(split(fa)).flatMap { case (a, ga) => + f(a, G.foldRight(ga, lb)(f)) + } + + def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = { + val (a, ga) = split(fa) + G.foldLeft(ga, f(a))((b, a) => g(b, a)) + } + + def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = + Always(split(fa)).flatMap { case (a, ga) => + G.reduceRightToOption(ga)(f)(g).flatMap { + case Some(b) => g(a, Now(b)) + case None => Later(f(a)) + } + } + + override def size[A](fa: F[A]): Long = { + val (_, tail) = split(fa) + 1 + G.size(tail) + } + + override def fold[A](fa: F[A])(implicit A: Monoid[A]): A = { + val (a, ga) = split(fa) + A.combine(a, G.fold(ga)) + } + + override def foldM[H[_], A, B](fa: F[A], z: B)(f: (B, A) => H[B])(implicit H: Monad[H]): H[B] = { + val (a, ga) = split(fa) + H.flatMap(f(z, a))(G.foldM(ga, _)(f)) + } + + override def find[A](fa: F[A])(f: A => Boolean): Option[A] = { + val (a, ga) = split(fa) + if (f(a)) Some(a) else G.find(ga)(f) + } + + override def exists[A](fa: F[A])(p: A => Boolean): Boolean = { + val (a, ga) = split(fa) + p(a) || G.exists(ga)(p) + } + + override def forall[A](fa: F[A])(p: A => Boolean): Boolean = { + val (a, ga) = split(fa) + p(a) && G.forall(ga)(p) + } + + override def toList[A](fa: F[A]): List[A] = { + val (a, ga) = split(fa) + a :: G.toList(ga) + } + + override def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] = { + val (a, ga) = split(fa) + NonEmptyList(a, G.toList(ga)) + } + + override def filter_[A](fa: F[A])(p: A => Boolean): List[A] = { + val (a, ga) = split(fa) + val filteredTail = G.filter_(ga)(p) + if (p(a)) a :: filteredTail else filteredTail + } + + override def takeWhile_[A](fa: F[A])(p: A => Boolean): List[A] = { + val (a, ga) = split(fa) + if (p(a)) a :: G.takeWhile_(ga)(p) else Nil + } + + override def dropWhile_[A](fa: F[A])(p: A => Boolean): List[A] = { + val (a, ga) = split(fa) + if (p(a)) G.dropWhile_(ga)(p) else a :: G.toList(ga) + } +} diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index f0bfa0bccd..86a6d6c6f8 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -22,14 +22,12 @@ import simulacrum.typeclass def flatSequence[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] = G.map(traverse1(fgfa)(identity))(F.flatten) - override def traverse[G[_] : Applicative, A, B](fa: F[A])(f: (A) => G[B]): G[F[B]] = traverse1(fa)(f) override def sequence[G[_] : Applicative, A](fga: F[G[A]]): G[F[A]] = traverse1(fga)(identity) - override def reduceMap[A, B](fa: F[A])(f: (A) => B)(implicit B: Semigroup[B]): B = reduceLeft(traverse1[Id, A, B](fa)(f))(B.combine) diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 96a7875380..569a74f737 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -98,7 +98,7 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) { s"OneAnd(${A.show(head)}, ${FA.show(tail)})" } -private[data] sealed trait OneAndInstances { +private[data] sealed trait OneAndInstances extends OneAndLowPriority2 { implicit def catsDataEqForOneAnd[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] = new Eq[OneAnd[F, A]]{ @@ -117,31 +117,11 @@ private[data] sealed trait OneAndInstances { implicit def catsDataSemigroupForOneAnd[F[_]: Alternative, A]: Semigroup[OneAnd[F, A]] = catsDataSemigroupKForOneAnd[F].algebra - implicit def catsDataTraverseForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = - new Traverse1[OneAnd[F, ?]] { - - override def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { - import cats.syntax.cartesian._ - - fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) - .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) - } + implicit def catsDataReducibleForOneAnd[F[_]](implicit F: Foldable[F]): Reducible[OneAnd[F, ?]] = + new NonEmptyReducible[OneAnd[F, ?], F] { + override def split[A](fa: OneAnd[F, A]): (A, F[A]) = (fa.head, fa.tail) - def reduceLeftTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (B, A) => B): B = { - fa.foldLeft(f(fa.head))(g) - } - - def reduceRightTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = { - fa.foldRight(Always(f(fa.head)))(g) - } - - def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { - fa.foldLeft(b)(f) - } - - def foldRight[A, B](fa: OneAnd[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { - fa.foldRight(lb)(f) - } + override def size[A](fa: OneAnd[F, A]): Long = 1 + F.size(fa.tail) } implicit def catsDataMonadForOneAnd[F[_]](implicit monad: MonadCombine[F]): Monad[OneAnd[F, ?]] = @@ -225,4 +205,32 @@ private[data] trait OneAndLowPriority1 extends OneAndLowPriority0 { } +private[data] trait OneAndLowPriority2 extends OneAndLowPriority1 { + implicit def catsDataTraverse1ForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = + new Traverse1[OneAnd[F, ?]] { + override def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { + import cats.syntax.cartesian._ + + fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) + .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) + } + + def reduceLeftTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (B, A) => B): B = { + fa.foldLeft(f(fa.head))(g) + } + + def reduceRightTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.foldRight(Always(f(fa.head)))(g) + } + + def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { + fa.foldLeft(b)(f) + } + + def foldRight[A, B](fa: OneAnd[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.foldRight(lb)(f) + } + } +} + object OneAnd extends OneAndInstances From 11e8a5d3b6a9b5c5b9f696fc3a34c2688176066f Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Thu, 18 May 2017 21:21:38 +0200 Subject: [PATCH 08/28] Use NonEmptyTraverse1 for traverse1 implementation of OneAnd --- core/src/main/scala/cats/Traverse1.scala | 4 ++-- core/src/main/scala/cats/data/OneAnd.scala | 28 ++++++---------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index 86a6d6c6f8..69b28d444a 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -37,13 +37,13 @@ import simulacrum.typeclass } /** - * This class defines a `Traverse1[F]` in terms of a `Foldable[G] with Traverse[G]` + * This class defines a `Traverse1[F]` in terms of a `Traverse[G]` * together with a `split` method, `F[A]` => `(A, G[A])`. * * This class can be used on any type where the first value (`A`) and * the "rest" of the values (`G[A]`) can be easily found. */ -abstract class NonEmptyTraverse1[F[_], G[_]](implicit G: Foldable[G] with Traverse[G]) extends Traverse1[F] { +abstract class NonEmptyTraverse1[F[_], G[_]](implicit G: Traverse[G]) extends Traverse1[F] { def split[A](fa: F[A]): (A, G[A]) def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = { diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 569a74f737..3aac64b759 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -207,29 +207,15 @@ private[data] trait OneAndLowPriority1 extends OneAndLowPriority0 { private[data] trait OneAndLowPriority2 extends OneAndLowPriority1 { implicit def catsDataTraverse1ForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = - new Traverse1[OneAnd[F, ?]] { - override def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { - import cats.syntax.cartesian._ + new NonEmptyTraverse1[OneAnd[F, ?], F] { + def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { + import cats.syntax.cartesian._ - fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) - .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) - } - - def reduceLeftTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (B, A) => B): B = { - fa.foldLeft(f(fa.head))(g) - } - - def reduceRightTo[A, B](fa: OneAnd[F, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = { - fa.foldRight(Always(f(fa.head)))(g) - } - - def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { - fa.foldLeft(b)(f) - } + fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) + .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) + } - def foldRight[A, B](fa: OneAnd[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { - fa.foldRight(lb)(f) - } + def split[A](fa: OneAnd[F, A]): (A, F[A]) = (fa.head, fa.tail) } } From 6945a269d7897318af8e0c02903a82ba60926eb3 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 13:47:16 +0200 Subject: [PATCH 09/28] Rename flatSequence --- core/src/main/scala/cats/Traverse1.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index 69b28d444a..aca0c7e0d6 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -19,7 +19,7 @@ import simulacrum.typeclass def flatTraverse1[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Apply[G], F: FlatMap[F]): G[F[B]] = G.map(traverse1(fa)(f))(F.flatten) - def flatSequence[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] = + def flatSequence1[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] = G.map(traverse1(fgfa)(identity))(F.flatten) override def traverse[G[_] : Applicative, A, B](fa: F[A])(f: (A) => G[B]): G[F[B]] = From 17d8d625454b34a499b8a84c0248c5001304f653 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 14:15:19 +0200 Subject: [PATCH 10/28] Remove NonEmptyReducible class and replace with NonEmptyReducible + Traverse1 --- core/src/main/scala/cats/Traverse1.scala | 101 ------------------ .../main/scala/cats/data/NonEmptyList.scala | 4 +- .../main/scala/cats/data/NonEmptyVector.scala | 4 +- core/src/main/scala/cats/data/OneAnd.scala | 2 +- 4 files changed, 5 insertions(+), 106 deletions(-) diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index aca0c7e0d6..56dbea34e2 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -1,6 +1,5 @@ package cats -import cats.data.NonEmptyList import simulacrum.typeclass /** @@ -25,106 +24,6 @@ import simulacrum.typeclass override def traverse[G[_] : Applicative, A, B](fa: F[A])(f: (A) => G[B]): G[F[B]] = traverse1(fa)(f) - override def sequence[G[_] : Applicative, A](fga: F[G[A]]): G[F[A]] = - traverse1(fga)(identity) - - override def reduceMap[A, B](fa: F[A])(f: (A) => B)(implicit B: Semigroup[B]): B = - reduceLeft(traverse1[Id, A, B](fa)(f))(B.combine) - - override def map[A, B](fa: F[A])(f: A => B): F[B] = - traverse1[Id, A, B](fa)(f) - -} - -/** - * This class defines a `Traverse1[F]` in terms of a `Traverse[G]` - * together with a `split` method, `F[A]` => `(A, G[A])`. - * - * This class can be used on any type where the first value (`A`) and - * the "rest" of the values (`G[A]`) can be easily found. - */ -abstract class NonEmptyTraverse1[F[_], G[_]](implicit G: Traverse[G]) extends Traverse1[F] { - def split[A](fa: F[A]): (A, G[A]) - - def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = { - val (a, ga) = split(fa) - G.foldLeft(ga, f(b, a))(f) - } - - def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - Always(split(fa)).flatMap { case (a, ga) => - f(a, G.foldRight(ga, lb)(f)) - } - - def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = { - val (a, ga) = split(fa) - G.foldLeft(ga, f(a))((b, a) => g(b, a)) - } - - def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = - Always(split(fa)).flatMap { case (a, ga) => - G.reduceRightToOption(ga)(f)(g).flatMap { - case Some(b) => g(a, Now(b)) - case None => Later(f(a)) - } - } - - override def size[A](fa: F[A]): Long = { - val (_, tail) = split(fa) - 1 + G.size(tail) - } - - override def fold[A](fa: F[A])(implicit A: Monoid[A]): A = { - val (a, ga) = split(fa) - A.combine(a, G.fold(ga)) - } - - override def foldM[H[_], A, B](fa: F[A], z: B)(f: (B, A) => H[B])(implicit H: Monad[H]): H[B] = { - val (a, ga) = split(fa) - H.flatMap(f(z, a))(G.foldM(ga, _)(f)) - } - - override def find[A](fa: F[A])(f: A => Boolean): Option[A] = { - val (a, ga) = split(fa) - if (f(a)) Some(a) else G.find(ga)(f) - } - - override def exists[A](fa: F[A])(p: A => Boolean): Boolean = { - val (a, ga) = split(fa) - p(a) || G.exists(ga)(p) - } - - override def forall[A](fa: F[A])(p: A => Boolean): Boolean = { - val (a, ga) = split(fa) - p(a) && G.forall(ga)(p) - } - - override def toList[A](fa: F[A]): List[A] = { - val (a, ga) = split(fa) - a :: G.toList(ga) - } - - override def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] = { - val (a, ga) = split(fa) - NonEmptyList(a, G.toList(ga)) - } - - override def filter_[A](fa: F[A])(p: A => Boolean): List[A] = { - val (a, ga) = split(fa) - val filteredTail = G.filter_(ga)(p) - if (p(a)) a :: filteredTail else filteredTail - } - - override def takeWhile_[A](fa: F[A])(p: A => Boolean): List[A] = { - val (a, ga) = split(fa) - if (p(a)) a :: G.takeWhile_(ga)(p) else Nil - } - - override def dropWhile_[A](fa: F[A])(p: A => Boolean): List[A] = { - val (a, ga) = split(fa) - if (p(a)) G.dropWhile_(ga)(p) else a :: G.toList(ga) - } - } diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 3da0ebdd16..90f3ecec0b 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -326,8 +326,8 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 implicit val catsDataInstancesForNonEmptyList: SemigroupK[NonEmptyList] with Reducible[NonEmptyList] with Comonad[NonEmptyList] with Traverse1[NonEmptyList] with Monad[NonEmptyList] = - new NonEmptyTraverse1[NonEmptyList, List] with SemigroupK[NonEmptyList] with Comonad[NonEmptyList] - with Monad[NonEmptyList] { + new NonEmptyReducible[NonEmptyList, List] with SemigroupK[NonEmptyList] with Comonad[NonEmptyList] + with Monad[NonEmptyList] with Traverse1[NonEmptyList] { def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] = a concat b diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index 03cde48f27..1ad75bf530 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -192,8 +192,8 @@ private[data] sealed trait NonEmptyVectorInstances { implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector] with Reducible[NonEmptyVector] with Comonad[NonEmptyVector] with Traverse[NonEmptyVector] with Monad[NonEmptyVector] = - new NonEmptyTraverse1[NonEmptyVector, Vector] with SemigroupK[NonEmptyVector] with Comonad[NonEmptyVector] - with Monad[NonEmptyVector] { + new NonEmptyReducible[NonEmptyVector, Vector] with SemigroupK[NonEmptyVector] with Comonad[NonEmptyVector] + with Monad[NonEmptyVector] with Traverse1[NonEmptyVector] { def combineK[A](a: NonEmptyVector[A], b: NonEmptyVector[A]): NonEmptyVector[A] = a concatNev b diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 3aac64b759..bc03f09960 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -207,7 +207,7 @@ private[data] trait OneAndLowPriority1 extends OneAndLowPriority0 { private[data] trait OneAndLowPriority2 extends OneAndLowPriority1 { implicit def catsDataTraverse1ForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = - new NonEmptyTraverse1[OneAnd[F, ?], F] { + new NonEmptyReducible[OneAnd[F, ?], F] with Traverse1[OneAnd[F, ?]] { def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { import cats.syntax.cartesian._ From b1e115bc98f9e7c4bd47e187ba60b2f97dbe3d79 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 14:15:44 +0200 Subject: [PATCH 11/28] Replace traverse1 with more performant implementation --- core/src/main/scala/cats/data/NonEmptyList.scala | 13 +++++++------ core/src/main/scala/cats/data/NonEmptyVector.scala | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 90f3ecec0b..b69d76e5d7 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -3,8 +3,6 @@ package data import cats.instances.list._ import cats.syntax.order._ -import cats.syntax.semigroup._ -import cats.syntax.cartesian._ import scala.annotation.tailrec import scala.collection.immutable.TreeSet @@ -354,10 +352,13 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 def extract[A](fa: NonEmptyList[A]): A = fa.head - def traverse1[G[_] : Apply, A, B](as: NonEmptyList[A])(f: A => G[B]): G[NonEmptyList[B]] = { - as.map(a => Apply[G].map(f(a))(NonEmptyList.of(_))) - .reduceLeft((acc, a) => (acc |@| a).map( _ |+| _ )) - } + def traverse1[G[_], A, B](nel: NonEmptyList[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyList[B]] = + Foldable[List].reduceRightToOption[A, G[List[B]]](nel.tail)(a => G.map(f(a))(_ :: Nil)) { (a, lglb) => + G.map2Eval(f(a), lglb)(_ :: _) + }.map { + case None => G.map(f(nel.head))(NonEmptyList(_, Nil)) + case Some(gtail) => G.map2(f(nel.head), gtail)(NonEmptyList(_, _)) + }.value override def traverse[G[_], A, B](fa: NonEmptyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] = fa traverse f diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index 1ad75bf530..e650918bf3 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -4,8 +4,6 @@ package data import scala.annotation.tailrec import scala.collection.immutable.{TreeSet, VectorBuilder} import cats.instances.vector._ -import cats.syntax.cartesian._ -import cats.syntax.semigroup._ /** * A data type which represents a `Vector` guaranteed to contain at least one element. @@ -228,10 +226,13 @@ private[data] sealed trait NonEmptyVectorInstances { def extract[A](fa: NonEmptyVector[A]): A = fa.head - def traverse1[G[_] : Apply, A, B](as: NonEmptyVector[A])(f: A => G[B]): G[NonEmptyVector[B]] = { - as.map(a => Apply[G].map(f(a))(NonEmptyVector.of(_))) - .reduceLeft((acc, a) => (acc |@| a).map( _ |+| _ )) - } + def traverse1[G[_], A, B](nel: NonEmptyVector[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyVector[B]] = + Foldable[Vector].reduceRightToOption[A, G[Vector[B]]](nel.tail)(a => G.map(f(a))(_ +: Vector.empty)) { (a, lglb) => + G.map2Eval(f(a), lglb)(_ +: _) + }.map { + case None => G.map(f(nel.head))(NonEmptyVector(_, Vector.empty)) + case Some(gtail) => G.map2(f(nel.head), gtail)(NonEmptyVector(_, _)) + }.value override def foldLeft[A, B](fa: NonEmptyVector[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) From f437a9825221fa8c014dd90e6614b957c81c2753 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 15:05:23 +0200 Subject: [PATCH 12/28] Remove Unapply syntax --- core/src/main/scala/cats/syntax/reducible.scala | 15 ++++++++------- core/src/main/scala/cats/syntax/traverse1.scala | 9 +-------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/core/src/main/scala/cats/syntax/reducible.scala b/core/src/main/scala/cats/syntax/reducible.scala index e977e09735..5a062a802a 100644 --- a/core/src/main/scala/cats/syntax/reducible.scala +++ b/core/src/main/scala/cats/syntax/reducible.scala @@ -1,12 +1,13 @@ package cats package syntax -private[syntax] trait ReducibleSyntax1 { - implicit def catsSyntaxUReducible[FA](fa: FA)(implicit U: Unapply[Reducible, FA]): Reducible.Ops[U.M, U.A] = - new Reducible.Ops[U.M, U.A] { - val self = U.subst(fa) - val typeClassInstance = U.TC - } + +trait ReducibleSyntax extends Reducible.ToReducibleOps { + implicit final def catsSyntaxNestedReducible[F[_]: Reducible, G[_], A](fga: F[G[A]]): NestedReducibleOps[F, G, A] = + new NestedReducibleOps[F, G, A](fga) +} + +final class NestedReducibleOps[F[_], G[_], A](val fga: F[G[A]]) extends AnyVal { + def reduceK(implicit F: Reducible[F], G: SemigroupK[G]): G[A] = F.reduceK(fga) } -trait ReducibleSyntax extends Reducible.ToReducibleOps with ReducibleSyntax1 diff --git a/core/src/main/scala/cats/syntax/traverse1.scala b/core/src/main/scala/cats/syntax/traverse1.scala index 21b3b5e0dd..1f182d4dac 100644 --- a/core/src/main/scala/cats/syntax/traverse1.scala +++ b/core/src/main/scala/cats/syntax/traverse1.scala @@ -1,11 +1,4 @@ package cats package syntax -trait Traverse1Syntax extends Traverse1.ToTraverse1Ops { - implicit def catsSyntaxNestedTraverse1[F[_]: Traverse1, G[_], A](fga: F[G[A]]): NestedTraverse1Ops[F, G, A] = - new NestedTraverse1Ops[F, G, A](fga) -} - -final class NestedTraverse1Ops[F[_], G[_], A](fga: F[G[A]])(implicit F: Traverse1[F]) { - def reduceK(implicit G: SemigroupK[G]): G[A] = F.reduceK(fga) -} +trait Traverse1Syntax extends Traverse1.ToTraverse1Ops From 647df0637cb50341cd9f5c9191822a9dfb559395 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 16:29:19 +0200 Subject: [PATCH 13/28] Separate traverse and traverse1 for OneAnd --- core/src/main/scala/cats/data/OneAnd.scala | 25 ++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index b99f976787..856b6687f8 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -209,14 +209,31 @@ private[data] trait OneAndLowPriority1 extends OneAndLowPriority0 { } private[data] trait OneAndLowPriority2 extends OneAndLowPriority1 { + implicit def catsDataTraverseForOneAnd[F[_]](implicit F: Traverse[F]): Traverse[OneAnd[F, ?]] = + new Traverse[OneAnd[F, ?]] { + def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] = { + G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value + } + + def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { + fa.foldLeft(b)(f) + } + + def foldRight[A, B](fa: OneAnd[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.foldRight(lb)(f) + } + } +} + +private[data] trait OneAndLowPriority3 extends OneAndLowPriority2 { implicit def catsDataTraverse1ForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = new NonEmptyReducible[OneAnd[F, ?], F] with Traverse1[OneAnd[F, ?]] { def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { - import cats.syntax.cartesian._ + import cats.syntax.cartesian._ - fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) - .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) - } + fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) + .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) + } def split[A](fa: OneAnd[F, A]): (A, F[A]) = (fa.head, fa.tail) } From 1f69c30ace538e584ead9cf1633654fc5fc58cc1 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 16:37:09 +0200 Subject: [PATCH 14/28] Override traverse implementation of NonEmptyVector --- core/src/main/scala/cats/data/NonEmptyVector.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index 3226453615..4efd761656 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -234,6 +234,9 @@ private[data] sealed trait NonEmptyVectorInstances { case Some(gtail) => G.map2(f(nel.head), gtail)(NonEmptyVector(_, _)) }.value + override def traverse[G[_], A, B](fa: NonEmptyVector[A])(f: (A) => G[B])(implicit G: Applicative[G]): G[NonEmptyVector[B]] = + G.map2Eval(f(fa.head), Always(Traverse[Vector].traverse(fa.tail)(f)))(NonEmptyVector(_, _)).value + override def foldLeft[A, B](fa: NonEmptyVector[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) From caac5f7f93d6ba1c1994e0745d11b137906fa0f9 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 17:07:14 +0200 Subject: [PATCH 15/28] Change type annotation of NonEmptyvector instances to reflect actual type --- core/src/main/scala/cats/data/NonEmptyVector.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index 4efd761656..7bcff8aae7 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -189,7 +189,7 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A]) extends AnyVal private[data] sealed trait NonEmptyVectorInstances { implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector] with Reducible[NonEmptyVector] - with Comonad[NonEmptyVector] with Traverse[NonEmptyVector] with Monad[NonEmptyVector] = + with Comonad[NonEmptyVector] with Traverse1[NonEmptyVector] with Monad[NonEmptyVector] = new NonEmptyReducible[NonEmptyVector, Vector] with SemigroupK[NonEmptyVector] with Comonad[NonEmptyVector] with Monad[NonEmptyVector] with Traverse1[NonEmptyVector] { From aa5d6aacbb16863d8d99f0a43fd9d0696ecd61bd Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 17:19:45 +0200 Subject: [PATCH 16/28] Add Law tests for traverse1 instances of NonEmptyList and Vector --- tests/src/test/scala/cats/tests/NonEmptyListTests.scala | 3 ++- tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala index 7773f9d354..5a6b911c09 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala @@ -4,7 +4,7 @@ package tests import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.data.NonEmptyList -import cats.laws.discipline.{ComonadTests, SemigroupKTests, MonadTests, SerializableTests, TraverseTests, ReducibleTests} +import cats.laws.discipline.{ComonadTests, SemigroupKTests, MonadTests, SerializableTests, TraverseTests, Traverse1Tests, ReducibleTests} import cats.laws.discipline.arbitrary._ class NonEmptyListTests extends CatsSuite { @@ -15,6 +15,7 @@ class NonEmptyListTests extends CatsSuite { checkAll("NonEmptyList[Int]", OrderLaws[NonEmptyList[Int]].order) checkAll("NonEmptyList[Int] with Option", TraverseTests[NonEmptyList].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyList[Int] with Option", Traverse1Tests[NonEmptyList].traverse1[Option, Int, Int, Int, Int, Option, Option]) checkAll("Traverse[NonEmptyList[A]]", SerializableTests.serializable(Traverse[NonEmptyList])) checkAll("NonEmptyList[Int]", ReducibleTests[NonEmptyList].reducible[Option, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala b/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala index 0a0841e3fb..1e6a762f1c 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala @@ -6,7 +6,7 @@ import catalysts.Platform import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.data.NonEmptyVector -import cats.laws.discipline.{ComonadTests, SemigroupKTests, FoldableTests, SerializableTests, TraverseTests, ReducibleTests, MonadTests} +import cats.laws.discipline.{ComonadTests, SemigroupKTests, FoldableTests, SerializableTests, TraverseTests, Traverse1Tests, ReducibleTests, MonadTests} import cats.laws.discipline.arbitrary._ import scala.util.Properties @@ -19,6 +19,7 @@ class NonEmptyVectorTests extends CatsSuite { checkAll("NonEmptyVector[Int]", OrderLaws[NonEmptyVector[Int]].eqv) checkAll("NonEmptyVector[Int] with Option", TraverseTests[NonEmptyVector].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyVector[Int] with Option", Traverse1Tests[NonEmptyVector].traverse1[Option, Int, Int, Int, Int, Option, Option]) checkAll("Traverse[NonEmptyVector[A]]", SerializableTests.serializable(Traverse[NonEmptyVector])) checkAll("NonEmptyVector[Int]", ReducibleTests[NonEmptyVector].reducible[Option, Int, Int]) From 590ba54832efb7341a5473fc1f94bb4278afa121 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 17:23:06 +0200 Subject: [PATCH 17/28] Correct inheritance for OneAnd --- core/src/main/scala/cats/data/OneAnd.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 856b6687f8..59cf21a2cd 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -98,7 +98,7 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) { s"OneAnd(${A.show(head)}, ${FA.show(tail)})" } -private[data] sealed trait OneAndInstances extends OneAndLowPriority2 { +private[data] sealed trait OneAndInstances extends OneAndLowPriority3 { implicit def catsDataEqForOneAnd[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] = new Eq[OneAnd[F, A]]{ From dfbc0642b42f8c86936c6bfdec17c7306813e468 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 17:27:21 +0200 Subject: [PATCH 18/28] Add traverse1 law testing for OneAnd instance --- tests/src/test/scala/cats/tests/OneAndTests.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index f7a12fbe74..e994f57426 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -5,7 +5,7 @@ import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.instances.stream._ import cats.data.{NonEmptyStream, OneAnd} -import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, ReducibleTests} +import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, Traverse1Tests, ReducibleTests} import cats.laws.discipline.arbitrary._ class OneAndTests extends CatsSuite { @@ -16,6 +16,7 @@ class OneAndTests extends CatsSuite { checkAll("OneAnd[Stream, Int]", OrderLaws[OneAnd[Stream, Int]].eqv) checkAll("OneAnd[Stream, Int] with Option", TraverseTests[OneAnd[Stream, ?]].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("OneAnd[Stream, Int] with Option", Traverse1Tests[OneAnd[Stream, ?]].traverse1[Option, Int, Int, Int, Int, Option, Option]) checkAll("Traverse[OneAnd[Stream, A]]", SerializableTests.serializable(Traverse[OneAnd[Stream, ?]])) checkAll("OneAnd[Stream, Int]", ReducibleTests[OneAnd[Stream, ?]].reducible[Option, Int, Int]) From 9093a53e4cb34c302bb901e3c5de13027f272ad9 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 18:28:47 +0200 Subject: [PATCH 19/28] Add doctests for traverse1 and sequence1 --- core/src/main/scala/cats/Traverse1.scala | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index 56dbea34e2..783c5314f0 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -10,8 +10,33 @@ import simulacrum.typeclass */ @typeclass trait Traverse1[F[_]] extends Traverse[F] with Reducible[F] { + /** + * Given a function which returns a G effect, thread this effect + * through the running of this function on all the values in F, + * returning an F[B] in a G context. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> def countWords(words: List[String]): Map[String, Int] = words.groupBy(identity).mapValues(_.length) + * scala> NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do")).traverse1(countWords) + * res0:Map[String,cats.data.NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1)) + * }}} + */ def traverse1[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]] + /** + * Thread all the G effects through the F structure to invert the + * structure from F[G[A]] to G[F[A]]. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> val x = NonEmptyList(Map(do -> 1, How -> 1, you -> 1, fly -> 1), Map(do -> 2, you -> 1, What -> 1)) + * scala> x.sequence1 + * res0: Map[String,cats.data.NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1)) + * }}} + */ def sequence1[G[_]: Apply, A](fga: F[G[A]]): G[F[A]] = traverse1(fga)(identity) From 5c49d9f1b1557bea899605a46dbf68d3334db0c2 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 19:02:32 +0200 Subject: [PATCH 20/28] Add remaining doctests --- core/src/main/scala/cats/Traverse1.scala | 41 ++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index 783c5314f0..fad0555225 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -18,9 +18,10 @@ import simulacrum.typeclass * Example: * {{{ * scala> import cats.implicits._ + * scala> import cats.data.NonEmptyList * scala> def countWords(words: List[String]): Map[String, Int] = words.groupBy(identity).mapValues(_.length) * scala> NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do")).traverse1(countWords) - * res0:Map[String,cats.data.NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1)) + * res0: Map[String,cats.data.NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1)) * }}} */ def traverse1[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]] @@ -32,17 +33,51 @@ import simulacrum.typeclass * Example: * {{{ * scala> import cats.implicits._ - * scala> val x = NonEmptyList(Map(do -> 1, How -> 1, you -> 1, fly -> 1), Map(do -> 2, you -> 1, What -> 1)) + * scala> import cats.data.NonEmptyList + * scala> val x = NonEmptyList.of(Map("do" -> 1, "you" -> 1), Map("do" -> 2, "you" -> 1)) + * scala> val y = NonEmptyList.of(Map("How" -> 3, "do" -> 1, "you" -> 1), Map[String,Int]()) * scala> x.sequence1 - * res0: Map[String,cats.data.NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1)) + * res0: Map[String,NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1)) + * scala> y.sequence1 + * res1: Map[String,NonEmptyList[Int]] = Map() * }}} */ def sequence1[G[_]: Apply, A](fga: F[G[A]]): G[F[A]] = traverse1(fga)(identity) + + /** + * A traverse1 followed by flattening the inner result. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> import cats.data.NonEmptyList + * scala> def countWords(words: List[String]): Map[String, Int] = words.groupBy(identity).mapValues(_.length) + * scala> val x = NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do")) + * scala> x.flatTraverse1(_.groupByNel(identity)) + * res0: Map[String,cats.data.NonEmptyList[String]] = Map(do -> NonEmptyList(do, do, do), you -> NonEmptyList(you, you)) + * }}} + */ def flatTraverse1[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Apply[G], F: FlatMap[F]): G[F[B]] = G.map(traverse1(fa)(f))(F.flatten) + /** + * Thread all the G effects through the F structure and flatten to invert the + * structure from F[G[F[A]]] to G[F[A]]. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> import cats.data.NonEmptyList + * scala> val x = NonEmptyList.of(Map(0 ->NonEmptyList.of(1, 2)), Map(0 -> NonEmptyList.of(3))) + * scala> val y: NonEmptyList[Map[Int, NonEmptyList[Int]]] = NonEmptyList.of(Map(), Map(1 -> NonEmptyList.of(3))) + * scala> x.flatSequence1 + * res0: Map[Int,cats.data.NonEmptyList[Int]] = Map(0 -> NonEmptyList(1, 2, 3)) + * scala> y.flatSequence1 + * res1: Map[Int,cats.data.NonEmptyList[Int]] = Map() + * }}} + */ def flatSequence1[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] = G.map(traverse1(fgfa)(identity))(F.flatten) From 0cc45319b98be30c2109d0c8b852e2fa1ec596cf Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 21:30:08 +0200 Subject: [PATCH 21/28] Override traverse in traverse1 instance of OneAnd --- core/src/main/scala/cats/data/OneAnd.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 59cf21a2cd..f7ab5e2b43 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -235,6 +235,10 @@ private[data] trait OneAndLowPriority3 extends OneAndLowPriority2 { .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) } + override def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] = { + G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value + } + def split[A](fa: OneAnd[F, A]): (A, F[A]) = (fa.head, fa.tail) } } From a2bbee4a95c9aa7f082cee80079226b5479c5acc Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 21:30:28 +0200 Subject: [PATCH 22/28] Fix Indentation --- .../cats/laws/discipline/Traverse1Tests.scala | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala b/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala index 2ad8fe38dd..7b37dc6bbe 100644 --- a/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala +++ b/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala @@ -11,30 +11,30 @@ trait Traverse1Tests[F[_]] extends TraverseTests[F] with ReducibleTests[F] { def laws: Traverse1Laws[F] def traverse1[G[_]: Applicative, A: Arbitrary, B: Arbitrary, C: Arbitrary, M: Arbitrary, X[_]: Applicative, Y[_]: Applicative](implicit - ArbFA: Arbitrary[F[A]], - ArbXB: Arbitrary[X[B]], - ArbYB: Arbitrary[Y[B]], - ArbYC: Arbitrary[Y[C]], - ArbFB: Arbitrary[F[B]], - ArbFGA: Arbitrary[F[G[A]]], - ArbGB: Arbitrary[G[B]], - CogenA: Cogen[A], - CogenB: Cogen[B], - CogenC: Cogen[C], - CogenM: Cogen[M], - M: Monoid[M], - MB: Monoid[B], - EqFA: Eq[F[A]], - EqFC: Eq[F[C]], - EqG: Eq[G[Unit]], - EqM: Eq[M], - EqA: Eq[A], - EqB: Eq[B], - EqXYFC: Eq[X[Y[F[C]]]], - EqXFB: Eq[X[F[B]]], - EqYFB: Eq[Y[F[B]]], - EqOptionA: Eq[Option[A]] - ): RuleSet = { + ArbFA: Arbitrary[F[A]], + ArbXB: Arbitrary[X[B]], + ArbYB: Arbitrary[Y[B]], + ArbYC: Arbitrary[Y[C]], + ArbFB: Arbitrary[F[B]], + ArbFGA: Arbitrary[F[G[A]]], + ArbGB: Arbitrary[G[B]], + CogenA: Cogen[A], + CogenB: Cogen[B], + CogenC: Cogen[C], + CogenM: Cogen[M], + M: Monoid[M], + MB: Monoid[B], + EqFA: Eq[F[A]], + EqFC: Eq[F[C]], + EqG: Eq[G[Unit]], + EqM: Eq[M], + EqA: Eq[A], + EqB: Eq[B], + EqXYFC: Eq[X[Y[F[C]]]], + EqXFB: Eq[X[F[B]]], + EqYFB: Eq[Y[F[B]]], + EqOptionA: Eq[Option[A]] + ): RuleSet = { implicit def EqXFBYFB : Eq[(X[F[B]], Y[F[B]])] = new Eq[(X[F[B]], Y[F[B]])] { override def eqv(x: (X[F[B]], Y[F[B]]), y: (X[F[B]], Y[F[B]])): Boolean = EqXFB.eqv(x._1, y._1) && EqYFB.eqv(x._2, y._2) From 8e9656b62f46e0e91cab75f7e410f0d458bb68df Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 28 May 2017 21:36:19 +0200 Subject: [PATCH 23/28] Remove redundant tests and replace with Traverse1 tests --- tests/src/test/scala/cats/tests/NonEmptyListTests.scala | 5 ++--- .../src/test/scala/cats/tests/NonEmptyVectorTests.scala | 5 ++--- tests/src/test/scala/cats/tests/OneAndTests.scala | 9 +++++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala index 5a6b911c09..72d77cc012 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala @@ -4,7 +4,7 @@ package tests import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.data.NonEmptyList -import cats.laws.discipline.{ComonadTests, SemigroupKTests, MonadTests, SerializableTests, TraverseTests, Traverse1Tests, ReducibleTests} +import cats.laws.discipline.{ComonadTests, SemigroupKTests, MonadTests, SerializableTests, Traverse1Tests, ReducibleTests} import cats.laws.discipline.arbitrary._ class NonEmptyListTests extends CatsSuite { @@ -14,9 +14,8 @@ class NonEmptyListTests extends CatsSuite { checkAll("NonEmptyList[Int]", OrderLaws[NonEmptyList[Int]].order) - checkAll("NonEmptyList[Int] with Option", TraverseTests[NonEmptyList].traverse[Int, Int, Int, Int, Option, Option]) checkAll("NonEmptyList[Int] with Option", Traverse1Tests[NonEmptyList].traverse1[Option, Int, Int, Int, Int, Option, Option]) - checkAll("Traverse[NonEmptyList[A]]", SerializableTests.serializable(Traverse[NonEmptyList])) + checkAll("Traverse1[NonEmptyList[A]]", SerializableTests.serializable(Traverse1[NonEmptyList])) checkAll("NonEmptyList[Int]", ReducibleTests[NonEmptyList].reducible[Option, Int, Int]) checkAll("Reducible[NonEmptyList]", SerializableTests.serializable(Reducible[NonEmptyList])) diff --git a/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala b/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala index 1e6a762f1c..059fb3167c 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala @@ -6,7 +6,7 @@ import catalysts.Platform import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.data.NonEmptyVector -import cats.laws.discipline.{ComonadTests, SemigroupKTests, FoldableTests, SerializableTests, TraverseTests, Traverse1Tests, ReducibleTests, MonadTests} +import cats.laws.discipline.{ComonadTests, SemigroupKTests, FoldableTests, SerializableTests, Traverse1Tests, ReducibleTests, MonadTests} import cats.laws.discipline.arbitrary._ import scala.util.Properties @@ -18,9 +18,8 @@ class NonEmptyVectorTests extends CatsSuite { checkAll("NonEmptyVector[Int]", OrderLaws[NonEmptyVector[Int]].eqv) - checkAll("NonEmptyVector[Int] with Option", TraverseTests[NonEmptyVector].traverse[Int, Int, Int, Int, Option, Option]) checkAll("NonEmptyVector[Int] with Option", Traverse1Tests[NonEmptyVector].traverse1[Option, Int, Int, Int, Int, Option, Option]) - checkAll("Traverse[NonEmptyVector[A]]", SerializableTests.serializable(Traverse[NonEmptyVector])) + checkAll("Traverse1[NonEmptyVector[A]]", SerializableTests.serializable(Traverse1[NonEmptyVector])) checkAll("NonEmptyVector[Int]", ReducibleTests[NonEmptyVector].reducible[Option, Int, Int]) checkAll("Reducible[NonEmptyVector]", SerializableTests.serializable(Reducible[NonEmptyVector])) diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index e994f57426..8e09970705 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -15,9 +15,14 @@ class OneAndTests extends CatsSuite { checkAll("OneAnd[Stream, Int]", OrderLaws[OneAnd[Stream, Int]].eqv) - checkAll("OneAnd[Stream, Int] with Option", TraverseTests[OneAnd[Stream, ?]].traverse[Int, Int, Int, Int, Option, Option]) checkAll("OneAnd[Stream, Int] with Option", Traverse1Tests[OneAnd[Stream, ?]].traverse1[Option, Int, Int, Int, Int, Option, Option]) - checkAll("Traverse[OneAnd[Stream, A]]", SerializableTests.serializable(Traverse[OneAnd[Stream, ?]])) + checkAll("Traverse1[OneAnd[Stream, A]]", SerializableTests.serializable(Traverse1[OneAnd[Stream, ?]])) + + { + implicit val traverse = ListWrapper.traverse + checkAll("OneAnd[ListWrapper, Int] with Option", TraverseTests[OneAnd[ListWrapper, ?]].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("Traverse[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Traverse[OneAnd[ListWrapper, ?]])) + } checkAll("OneAnd[Stream, Int]", ReducibleTests[OneAnd[Stream, ?]].reducible[Option, Int, Int]) checkAll("Reducible[OneAnd[Stream, ?]]", SerializableTests.serializable(Reducible[OneAnd[Stream, ?]])) From f214c64bc6d1bf495176b53fe9f75bd29bde52dc Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Mon, 29 May 2017 10:57:49 +0200 Subject: [PATCH 24/28] Add Traverse1#compose and instance for Nested --- core/src/main/scala/cats/Composed.scala | 8 ++++++++ core/src/main/scala/cats/Traverse1.scala | 7 ++++++- core/src/main/scala/cats/data/Nested.scala | 16 +++++++++++++++- .../src/test/scala/cats/tests/NestedTests.scala | 8 ++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/Composed.scala b/core/src/main/scala/cats/Composed.scala index e8f5552f1b..581cfc6233 100644 --- a/core/src/main/scala/cats/Composed.scala +++ b/core/src/main/scala/cats/Composed.scala @@ -71,6 +71,14 @@ private[cats] trait ComposedTraverse[F[_], G[_]] extends Traverse[λ[α => F[G[ F.traverse(fga)(ga => G.traverse(ga)(f)) } +private[cats] trait ComposedTraverse1[F[_], G[_]] extends Traverse1[λ[α => F[G[α]]]] with ComposedTraverse[F, G] with ComposedReducible[F, G] { + def F: Traverse1[F] + def G: Traverse1[G] + + override def traverse1[H[_]: Apply, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] = + F.traverse1(fga)(ga => G.traverse1(ga)(f)) +} + private[cats] trait ComposedTraverseFilter[F[_], G[_]] extends TraverseFilter[λ[α => F[G[α]]]] with ComposedTraverse[F, G] { def F: Traverse[F] def G: TraverseFilter[G] diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/Traverse1.scala index fad0555225..9dc02b408b 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/Traverse1.scala @@ -8,7 +8,7 @@ import simulacrum.typeclass * `Traverse1` is like a non-empty `Traverse`. In addition to the traverse and sequence * methods it provides traverse1 and sequence1 methods which require an `Apply` instance instead of `Applicative`. */ -@typeclass trait Traverse1[F[_]] extends Traverse[F] with Reducible[F] { +@typeclass trait Traverse1[F[_]] extends Traverse[F] with Reducible[F] { self => /** * Given a function which returns a G effect, thread this effect @@ -84,6 +84,11 @@ import simulacrum.typeclass override def traverse[G[_] : Applicative, A, B](fa: F[A])(f: (A) => G[B]): G[F[B]] = traverse1(fa)(f) + def compose[G[_]: Traverse1]: Traverse1[λ[α => F[G[α]]]] = + new ComposedTraverse1[F, G] { + val F = self + val G = Traverse1[G] + } } diff --git a/core/src/main/scala/cats/data/Nested.scala b/core/src/main/scala/cats/data/Nested.scala index 2d25b3bf85..e3f6a99da5 100644 --- a/core/src/main/scala/cats/data/Nested.scala +++ b/core/src/main/scala/cats/data/Nested.scala @@ -133,13 +133,20 @@ private[data] sealed abstract class NestedInstances9 extends NestedInstances10 { } } -private[data] sealed abstract class NestedInstances10 { +private[data] sealed abstract class NestedInstances10 extends NestedInstances11 { implicit def catsDataInvariantForNestedContravariant[F[_]: Invariant, G[_]: Contravariant]: Invariant[Nested[F, G, ?]] = new NestedInvariant[F, G] { val FG: Invariant[λ[α => F[G[α]]]] = Invariant[F].composeContravariant[G] } } +private[data] sealed abstract class NestedInstances11 { + implicit def catsDataTraverse1ForNested[F[_]: Traverse1, G[_]: Traverse1]: Traverse1[Nested[F, G, ?]] = + new NestedTraverse1[F, G] { + val FG: Traverse1[λ[α => F[G[α]]]] = Traverse1[F].compose[G] + } +} + private[data] trait NestedInvariant[F[_], G[_]] extends Invariant[Nested[F, G, ?]] { def FG: Invariant[λ[α => F[G[α]]]] @@ -233,6 +240,13 @@ private[data] trait NestedReducible[F[_], G[_]] extends Reducible[Nested[F, G, ? FG.reduceRightTo(fga.value)(f)(g) } +private[data] trait NestedTraverse1[F[_], G[_]] extends Traverse1[Nested[F, G, ?]] with NestedTraverse[F, G] with NestedReducible[F, G] { + def FG: Traverse1[λ[α => F[G[α]]]] + + override def traverse1[H[_]: Apply, A, B](fga: Nested[F, G, A])(f: A => H[B]): H[Nested[F, G, B]] = + Apply[H].map(FG.traverse1(fga.value)(f))(Nested(_)) +} + private[data] trait NestedContravariant[F[_], G[_]] extends Contravariant[Nested[F, G, ?]] { def FG: Contravariant[λ[α => F[G[α]]]] diff --git a/tests/src/test/scala/cats/tests/NestedTests.scala b/tests/src/test/scala/cats/tests/NestedTests.scala index 6f70f69fdb..ca14bdfc93 100644 --- a/tests/src/test/scala/cats/tests/NestedTests.scala +++ b/tests/src/test/scala/cats/tests/NestedTests.scala @@ -127,6 +127,14 @@ class NestedTests extends CatsSuite { checkAll("Nested[NonEmptyList, NonEmptyVector, ?]", ReducibleTests[Nested[NonEmptyList, NonEmptyVector, ?]].reducible[Option, Int, Int]) checkAll("Reducible[Nested[NonEmptyList, NonEmptyVector, ?]]", SerializableTests.serializable(Reducible[Nested[NonEmptyList, NonEmptyVector, ?]])) + { + //Traverse1 composition + checkAll("Nested[NonEmptyList, NonEmptyVector, ?]", Traverse1Tests[Nested[NonEmptyList, NonEmptyVector, ?]].traverse1[Option, Int, Int, Int, Int, Option, Option]) + checkAll("Traverse1[Nested[NonEmptyList, NonEmptyVector, ?]]", SerializableTests.serializable(Traverse1[Nested[NonEmptyList, NonEmptyVector, ?]])) + + } + + { // SemigroupK composition implicit val instance = ListWrapper.semigroupK From 9ed10eabb4d47d43d08063d307519744a3c6f39b Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Mon, 29 May 2017 11:54:12 +0200 Subject: [PATCH 25/28] Move nested traverse1 instance to NestedInstances --- core/src/main/scala/cats/data/Nested.scala | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/src/main/scala/cats/data/Nested.scala b/core/src/main/scala/cats/data/Nested.scala index e3f6a99da5..0de8238b8e 100644 --- a/core/src/main/scala/cats/data/Nested.scala +++ b/core/src/main/scala/cats/data/Nested.scala @@ -35,6 +35,11 @@ private[data] sealed abstract class NestedInstances extends NestedInstances0 { new NestedTraverseFilter[F, G] { val FG: TraverseFilter[λ[α => F[G[α]]]] = Traverse[F].composeFilter[G] } + + implicit def catsDataTraverse1ForNested[F[_]: Traverse1, G[_]: Traverse1]: Traverse1[Nested[F, G, ?]] = + new NestedTraverse1[F, G] { + val FG: Traverse1[λ[α => F[G[α]]]] = Traverse1[F].compose[G] + } } private[data] sealed abstract class NestedInstances0 extends NestedInstances1 { @@ -133,20 +138,13 @@ private[data] sealed abstract class NestedInstances9 extends NestedInstances10 { } } -private[data] sealed abstract class NestedInstances10 extends NestedInstances11 { +private[data] sealed abstract class NestedInstances10 { implicit def catsDataInvariantForNestedContravariant[F[_]: Invariant, G[_]: Contravariant]: Invariant[Nested[F, G, ?]] = new NestedInvariant[F, G] { val FG: Invariant[λ[α => F[G[α]]]] = Invariant[F].composeContravariant[G] } } -private[data] sealed abstract class NestedInstances11 { - implicit def catsDataTraverse1ForNested[F[_]: Traverse1, G[_]: Traverse1]: Traverse1[Nested[F, G, ?]] = - new NestedTraverse1[F, G] { - val FG: Traverse1[λ[α => F[G[α]]]] = Traverse1[F].compose[G] - } -} - private[data] trait NestedInvariant[F[_], G[_]] extends Invariant[Nested[F, G, ?]] { def FG: Invariant[λ[α => F[G[α]]]] From 7f55edc6fb5839506ab036c605ce80fc89bbacd6 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Mon, 29 May 2017 12:51:27 +0200 Subject: [PATCH 26/28] Move reducible nested tests --- tests/src/test/scala/cats/tests/NestedTests.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/src/test/scala/cats/tests/NestedTests.scala b/tests/src/test/scala/cats/tests/NestedTests.scala index ca14bdfc93..fb8a624d48 100644 --- a/tests/src/test/scala/cats/tests/NestedTests.scala +++ b/tests/src/test/scala/cats/tests/NestedTests.scala @@ -124,8 +124,11 @@ class NestedTests extends CatsSuite { checkAll("TraverseFilter[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(TraverseFilter[Nested[List, ListWrapper, ?]])) } - checkAll("Nested[NonEmptyList, NonEmptyVector, ?]", ReducibleTests[Nested[NonEmptyList, NonEmptyVector, ?]].reducible[Option, Int, Int]) - checkAll("Reducible[Nested[NonEmptyList, NonEmptyVector, ?]]", SerializableTests.serializable(Reducible[Nested[NonEmptyList, NonEmptyVector, ?]])) + { + implicit val foldable = ListWrapper.foldable + checkAll("Nested[NonEmptyList, OneAnd[ListWrapper, ?], ?]", ReducibleTests[Nested[NonEmptyList, OneAnd[ListWrapper, ?], ?]].reducible[Option, Int, Int]) + checkAll("Reducible[Nested[NonEmptyList, OneAnd[ListWrapper, ?], ?]]", SerializableTests.serializable(Reducible[Nested[NonEmptyList, OneAnd[ListWrapper, ?], ?]])) + } { //Traverse1 composition From 02c25d5c7dbf5549aec60ef949347997bdac31ef Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Thu, 8 Jun 2017 15:43:39 +0200 Subject: [PATCH 27/28] rename traverse1 to nonEmptyTraverse --- core/src/main/scala/cats/Composed.scala | 10 ++--- ...Traverse1.scala => NonEmptyTraverse.scala} | 44 +++++++++---------- core/src/main/scala/cats/Reducible.scala | 6 +-- core/src/main/scala/cats/data/Nested.scala | 14 +++--- .../main/scala/cats/data/NonEmptyList.scala | 6 +-- .../main/scala/cats/data/NonEmptyVector.scala | 6 +-- core/src/main/scala/cats/data/OneAnd.scala | 7 +-- core/src/main/scala/cats/package.scala | 6 +-- core/src/main/scala/cats/syntax/all.scala | 2 +- .../scala/cats/syntax/nonEmptyTraverse.scala | 4 ++ core/src/main/scala/cats/syntax/package.scala | 2 +- .../main/scala/cats/syntax/traverse1.scala | 4 -- ...1Laws.scala => NonEmptyTraverseLaws.scala} | 32 +++++++------- .../main/scala/cats/laws/ReducibleLaws.scala | 4 +- ...ests.scala => NonEmptyTraverseTests.scala} | 26 +++++------ .../cats/laws/discipline/ReducibleTests.scala | 4 +- .../test/scala/cats/tests/NestedTests.scala | 6 +-- .../scala/cats/tests/NonEmptyListTests.scala | 6 +-- .../cats/tests/NonEmptyVectorTests.scala | 6 +-- .../test/scala/cats/tests/OneAndTests.scala | 6 +-- .../test/scala/cats/tests/SyntaxTests.scala | 12 ++--- 21 files changed, 107 insertions(+), 106 deletions(-) rename core/src/main/scala/cats/{Traverse1.scala => NonEmptyTraverse.scala} (64%) create mode 100644 core/src/main/scala/cats/syntax/nonEmptyTraverse.scala delete mode 100644 core/src/main/scala/cats/syntax/traverse1.scala rename laws/src/main/scala/cats/laws/{Traverse1Laws.scala => NonEmptyTraverseLaws.scala} (64%) rename laws/src/main/scala/cats/laws/discipline/{Traverse1Tests.scala => NonEmptyTraverseTests.scala} (52%) diff --git a/core/src/main/scala/cats/Composed.scala b/core/src/main/scala/cats/Composed.scala index 581cfc6233..b231f13731 100644 --- a/core/src/main/scala/cats/Composed.scala +++ b/core/src/main/scala/cats/Composed.scala @@ -71,12 +71,12 @@ private[cats] trait ComposedTraverse[F[_], G[_]] extends Traverse[λ[α => F[G[ F.traverse(fga)(ga => G.traverse(ga)(f)) } -private[cats] trait ComposedTraverse1[F[_], G[_]] extends Traverse1[λ[α => F[G[α]]]] with ComposedTraverse[F, G] with ComposedReducible[F, G] { - def F: Traverse1[F] - def G: Traverse1[G] +private[cats] trait ComposedNonEmptyTraverse[F[_], G[_]] extends NonEmptyTraverse[λ[α => F[G[α]]]] with ComposedTraverse[F, G] with ComposedReducible[F, G] { + def F: NonEmptyTraverse[F] + def G: NonEmptyTraverse[G] - override def traverse1[H[_]: Apply, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] = - F.traverse1(fga)(ga => G.traverse1(ga)(f)) + override def nonEmptyTraverse[H[_]: Apply, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] = + F.nonEmptyTraverse(fga)(ga => G.nonEmptyTraverse(ga)(f)) } private[cats] trait ComposedTraverseFilter[F[_], G[_]] extends TraverseFilter[λ[α => F[G[α]]]] with ComposedTraverse[F, G] { diff --git a/core/src/main/scala/cats/Traverse1.scala b/core/src/main/scala/cats/NonEmptyTraverse.scala similarity index 64% rename from core/src/main/scala/cats/Traverse1.scala rename to core/src/main/scala/cats/NonEmptyTraverse.scala index 9dc02b408b..cc7516269d 100644 --- a/core/src/main/scala/cats/Traverse1.scala +++ b/core/src/main/scala/cats/NonEmptyTraverse.scala @@ -3,12 +3,12 @@ package cats import simulacrum.typeclass /** - * Traverse1, also known as Traversable1. + * NonEmptyTraverse, also known as Traversable1. * - * `Traverse1` is like a non-empty `Traverse`. In addition to the traverse and sequence - * methods it provides traverse1 and sequence1 methods which require an `Apply` instance instead of `Applicative`. + * `NonEmptyTraverse` is like a non-empty `Traverse`. In addition to the traverse and sequence + * methods it provides nonEmptyTraverse and nonEmptySequence methods which require an `Apply` instance instead of `Applicative`. */ -@typeclass trait Traverse1[F[_]] extends Traverse[F] with Reducible[F] { self => +@typeclass trait NonEmptyTraverse[F[_]] extends Traverse[F] with Reducible[F] { self => /** * Given a function which returns a G effect, thread this effect @@ -20,11 +20,11 @@ import simulacrum.typeclass * scala> import cats.implicits._ * scala> import cats.data.NonEmptyList * scala> def countWords(words: List[String]): Map[String, Int] = words.groupBy(identity).mapValues(_.length) - * scala> NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do")).traverse1(countWords) + * scala> NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do")).nonEmptyTraverse(countWords) * res0: Map[String,cats.data.NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1)) * }}} */ - def traverse1[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]] + def nonEmptyTraverse[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]] /** * Thread all the G effects through the F structure to invert the @@ -36,18 +36,18 @@ import simulacrum.typeclass * scala> import cats.data.NonEmptyList * scala> val x = NonEmptyList.of(Map("do" -> 1, "you" -> 1), Map("do" -> 2, "you" -> 1)) * scala> val y = NonEmptyList.of(Map("How" -> 3, "do" -> 1, "you" -> 1), Map[String,Int]()) - * scala> x.sequence1 + * scala> x.nonEmptySequence * res0: Map[String,NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1)) - * scala> y.sequence1 + * scala> y.nonEmptySequence * res1: Map[String,NonEmptyList[Int]] = Map() * }}} */ - def sequence1[G[_]: Apply, A](fga: F[G[A]]): G[F[A]] = - traverse1(fga)(identity) + def nonEmptySequence[G[_]: Apply, A](fga: F[G[A]]): G[F[A]] = + nonEmptyTraverse(fga)(identity) /** - * A traverse1 followed by flattening the inner result. + * A nonEmptyTraverse followed by flattening the inner result. * * Example: * {{{ @@ -55,12 +55,12 @@ import simulacrum.typeclass * scala> import cats.data.NonEmptyList * scala> def countWords(words: List[String]): Map[String, Int] = words.groupBy(identity).mapValues(_.length) * scala> val x = NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do")) - * scala> x.flatTraverse1(_.groupByNel(identity)) + * scala> x.nonEmptyFlatTraverse(_.groupByNel(identity)) * res0: Map[String,cats.data.NonEmptyList[String]] = Map(do -> NonEmptyList(do, do, do), you -> NonEmptyList(you, you)) * }}} */ - def flatTraverse1[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Apply[G], F: FlatMap[F]): G[F[B]] = - G.map(traverse1(fa)(f))(F.flatten) + def nonEmptyFlatTraverse[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Apply[G], F: FlatMap[F]): G[F[B]] = + G.map(nonEmptyTraverse(fa)(f))(F.flatten) /** * Thread all the G effects through the F structure and flatten to invert the @@ -72,22 +72,22 @@ import simulacrum.typeclass * scala> import cats.data.NonEmptyList * scala> val x = NonEmptyList.of(Map(0 ->NonEmptyList.of(1, 2)), Map(0 -> NonEmptyList.of(3))) * scala> val y: NonEmptyList[Map[Int, NonEmptyList[Int]]] = NonEmptyList.of(Map(), Map(1 -> NonEmptyList.of(3))) - * scala> x.flatSequence1 + * scala> x.nonEmptyFlatSequence * res0: Map[Int,cats.data.NonEmptyList[Int]] = Map(0 -> NonEmptyList(1, 2, 3)) - * scala> y.flatSequence1 + * scala> y.nonEmptyFlatSequence * res1: Map[Int,cats.data.NonEmptyList[Int]] = Map() * }}} */ - def flatSequence1[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] = - G.map(traverse1(fgfa)(identity))(F.flatten) + def nonEmptyFlatSequence[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] = + G.map(nonEmptyTraverse(fgfa)(identity))(F.flatten) override def traverse[G[_] : Applicative, A, B](fa: F[A])(f: (A) => G[B]): G[F[B]] = - traverse1(fa)(f) + nonEmptyTraverse(fa)(f) - def compose[G[_]: Traverse1]: Traverse1[λ[α => F[G[α]]]] = - new ComposedTraverse1[F, G] { + def compose[G[_]: NonEmptyTraverse]: NonEmptyTraverse[λ[α => F[G[α]]]] = + new ComposedNonEmptyTraverse[F, G] { val F = self - val G = Traverse1[G] + val G = NonEmptyTraverse[G] } diff --git a/core/src/main/scala/cats/Reducible.scala b/core/src/main/scala/cats/Reducible.scala index 91e8d1e4b6..8cd56d9341 100644 --- a/core/src/main/scala/cats/Reducible.scala +++ b/core/src/main/scala/cats/Reducible.scala @@ -127,7 +127,7 @@ import simulacrum.typeclass * available for `G` and want to take advantage of short-circuiting * the traversal. */ - def traverse1_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Apply[G]): G[Unit] = + def nonEmptyTraverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Apply[G]): G[Unit] = G.map(reduceLeftTo(fa)(f)((x, y) => G.map2(x, f(y))((_, b) => b)))(_ => ()) /** @@ -135,9 +135,9 @@ import simulacrum.typeclass * * This method is similar to [[Foldable.sequence_]] but requires only * an [[Apply]] instance for `G` instead of [[Applicative]]. See the - * [[traverse1_]] documentation for a description of the differences. + * [[nonEmptyTraverse_]] documentation for a description of the differences. */ - def sequence1_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] = + def nonEmptySequence_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] = G.map(reduceLeft(fga)((x, y) => G.map2(x, y)((_, b) => b)))(_ => ()) def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] = diff --git a/core/src/main/scala/cats/data/Nested.scala b/core/src/main/scala/cats/data/Nested.scala index 0de8238b8e..d8d6fc54df 100644 --- a/core/src/main/scala/cats/data/Nested.scala +++ b/core/src/main/scala/cats/data/Nested.scala @@ -36,9 +36,9 @@ private[data] sealed abstract class NestedInstances extends NestedInstances0 { val FG: TraverseFilter[λ[α => F[G[α]]]] = Traverse[F].composeFilter[G] } - implicit def catsDataTraverse1ForNested[F[_]: Traverse1, G[_]: Traverse1]: Traverse1[Nested[F, G, ?]] = - new NestedTraverse1[F, G] { - val FG: Traverse1[λ[α => F[G[α]]]] = Traverse1[F].compose[G] + implicit def catsDataNonEmptyTraverseForNested[F[_]: NonEmptyTraverse, G[_]: NonEmptyTraverse]: NonEmptyTraverse[Nested[F, G, ?]] = + new NestedNonEmptyTraverse[F, G] { + val FG: NonEmptyTraverse[λ[α => F[G[α]]]] = NonEmptyTraverse[F].compose[G] } } @@ -238,11 +238,11 @@ private[data] trait NestedReducible[F[_], G[_]] extends Reducible[Nested[F, G, ? FG.reduceRightTo(fga.value)(f)(g) } -private[data] trait NestedTraverse1[F[_], G[_]] extends Traverse1[Nested[F, G, ?]] with NestedTraverse[F, G] with NestedReducible[F, G] { - def FG: Traverse1[λ[α => F[G[α]]]] +private[data] trait NestedNonEmptyTraverse[F[_], G[_]] extends NonEmptyTraverse[Nested[F, G, ?]] with NestedTraverse[F, G] with NestedReducible[F, G] { + def FG: NonEmptyTraverse[λ[α => F[G[α]]]] - override def traverse1[H[_]: Apply, A, B](fga: Nested[F, G, A])(f: A => H[B]): H[Nested[F, G, B]] = - Apply[H].map(FG.traverse1(fga.value)(f))(Nested(_)) + override def nonEmptyTraverse[H[_]: Apply, A, B](fga: Nested[F, G, A])(f: A => H[B]): H[Nested[F, G, B]] = + Apply[H].map(FG.nonEmptyTraverse(fga.value)(f))(Nested(_)) } private[data] trait NestedContravariant[F[_], G[_]] extends Contravariant[Nested[F, G, ?]] { diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 3327a3627e..01b85be024 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -365,9 +365,9 @@ object NonEmptyList extends NonEmptyListInstances { private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 { implicit val catsDataInstancesForNonEmptyList: SemigroupK[NonEmptyList] with Reducible[NonEmptyList] - with Comonad[NonEmptyList] with Traverse1[NonEmptyList] with Monad[NonEmptyList] = + with Comonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] with Monad[NonEmptyList] = new NonEmptyReducible[NonEmptyList, List] with SemigroupK[NonEmptyList] with Comonad[NonEmptyList] - with Monad[NonEmptyList] with Traverse1[NonEmptyList] { + with Monad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] { def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] = a concat b @@ -394,7 +394,7 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 def extract[A](fa: NonEmptyList[A]): A = fa.head - def traverse1[G[_], A, B](nel: NonEmptyList[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyList[B]] = + def nonEmptyTraverse[G[_], A, B](nel: NonEmptyList[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyList[B]] = Foldable[List].reduceRightToOption[A, G[List[B]]](nel.tail)(a => G.map(f(a))(_ :: Nil)) { (a, lglb) => G.map2Eval(f(a), lglb)(_ :: _) }.map { diff --git a/core/src/main/scala/cats/data/NonEmptyVector.scala b/core/src/main/scala/cats/data/NonEmptyVector.scala index 7bcff8aae7..f4be7641a1 100644 --- a/core/src/main/scala/cats/data/NonEmptyVector.scala +++ b/core/src/main/scala/cats/data/NonEmptyVector.scala @@ -189,9 +189,9 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A]) extends AnyVal private[data] sealed trait NonEmptyVectorInstances { implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector] with Reducible[NonEmptyVector] - with Comonad[NonEmptyVector] with Traverse1[NonEmptyVector] with Monad[NonEmptyVector] = + with Comonad[NonEmptyVector] with NonEmptyTraverse[NonEmptyVector] with Monad[NonEmptyVector] = new NonEmptyReducible[NonEmptyVector, Vector] with SemigroupK[NonEmptyVector] with Comonad[NonEmptyVector] - with Monad[NonEmptyVector] with Traverse1[NonEmptyVector] { + with Monad[NonEmptyVector] with NonEmptyTraverse[NonEmptyVector] { def combineK[A](a: NonEmptyVector[A], b: NonEmptyVector[A]): NonEmptyVector[A] = a concatNev b @@ -226,7 +226,7 @@ private[data] sealed trait NonEmptyVectorInstances { def extract[A](fa: NonEmptyVector[A]): A = fa.head - def traverse1[G[_], A, B](nel: NonEmptyVector[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyVector[B]] = + def nonEmptyTraverse[G[_], A, B](nel: NonEmptyVector[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyVector[B]] = Foldable[Vector].reduceRightToOption[A, G[Vector[B]]](nel.tail)(a => G.map(f(a))(_ +: Vector.empty)) { (a, lglb) => G.map2Eval(f(a), lglb)(_ +: _) }.map { diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index f7ab5e2b43..8e7728004c 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -226,15 +226,16 @@ private[data] trait OneAndLowPriority2 extends OneAndLowPriority1 { } private[data] trait OneAndLowPriority3 extends OneAndLowPriority2 { - implicit def catsDataTraverse1ForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] = - new NonEmptyReducible[OneAnd[F, ?], F] with Traverse1[OneAnd[F, ?]] { - def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { + implicit def catsDataNonEmptyTraverseForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): NonEmptyTraverse[OneAnd[F, ?]] = + new NonEmptyReducible[OneAnd[F, ?], F] with NonEmptyTraverse[OneAnd[F, ?]] { + def nonEmptyTraverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = { import cats.syntax.cartesian._ fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F) .reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y)))) } + override def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] = { G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value } diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index 3082492762..88f71bae38 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -32,8 +32,8 @@ package object cats { * encodes pure unary function application. */ type Id[A] = A - implicit val catsInstancesForId: Bimonad[Id] with Monad[Id] with Traverse1[Id] = - new Bimonad[Id] with Monad[Id] with Traverse1[Id] { + implicit val catsInstancesForId: Bimonad[Id] with Monad[Id] with NonEmptyTraverse[Id] = + new Bimonad[Id] with Monad[Id] with NonEmptyTraverse[Id] { def pure[A](a: A): A = a def extract[A](a: A): A = a def flatMap[A, B](a: A)(f: A => B): B = f(a) @@ -51,7 +51,7 @@ package object cats { def foldLeft[A, B](a: A, b: B)(f: (B, A) => B) = f(b, a) def foldRight[A, B](a: A, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = f(a, lb) - def traverse1[G[_], A, B](a: A)(f: A => G[B])(implicit G: Apply[G]): G[B] = + def nonEmptyTraverse[G[_], A, B](a: A)(f: A => G[B])(implicit G: Apply[G]): G[B] = f(a) override def foldMap[A, B](fa: Id[A])(f: A => B)(implicit B: Monoid[B]): B = f(fa) override def reduce[A](fa: Id[A])(implicit A: Semigroup[A]): A = diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index 0e7d803b1a..0cf3dad050 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -42,7 +42,7 @@ trait AllSyntax with StrongSyntax with TraverseFilterSyntax with TraverseSyntax - with Traverse1Syntax + with NonEmptyTraverseSyntax with TupleSyntax with ValidatedSyntax with VectorSyntax diff --git a/core/src/main/scala/cats/syntax/nonEmptyTraverse.scala b/core/src/main/scala/cats/syntax/nonEmptyTraverse.scala new file mode 100644 index 0000000000..5c6fe63b36 --- /dev/null +++ b/core/src/main/scala/cats/syntax/nonEmptyTraverse.scala @@ -0,0 +1,4 @@ +package cats +package syntax + +trait NonEmptyTraverseSyntax extends NonEmptyTraverse.ToNonEmptyTraverseOps diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index 9aa4baf38f..7c7ab4285e 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -41,7 +41,7 @@ package object syntax { object strong extends StrongSyntax object monadTrans extends MonadTransSyntax object traverse extends TraverseSyntax - object traverse1 extends Traverse1Syntax + object nonEmptyTraverse extends NonEmptyTraverseSyntax object traverseFilter extends TraverseFilterSyntax object tuple extends TupleSyntax object validated extends ValidatedSyntax diff --git a/core/src/main/scala/cats/syntax/traverse1.scala b/core/src/main/scala/cats/syntax/traverse1.scala deleted file mode 100644 index 1f182d4dac..0000000000 --- a/core/src/main/scala/cats/syntax/traverse1.scala +++ /dev/null @@ -1,4 +0,0 @@ -package cats -package syntax - -trait Traverse1Syntax extends Traverse1.ToTraverse1Ops diff --git a/laws/src/main/scala/cats/laws/Traverse1Laws.scala b/laws/src/main/scala/cats/laws/NonEmptyTraverseLaws.scala similarity index 64% rename from laws/src/main/scala/cats/laws/Traverse1Laws.scala rename to laws/src/main/scala/cats/laws/NonEmptyTraverseLaws.scala index e498289482..cf0423cb2b 100644 --- a/laws/src/main/scala/cats/laws/Traverse1Laws.scala +++ b/laws/src/main/scala/cats/laws/NonEmptyTraverseLaws.scala @@ -1,19 +1,19 @@ package cats.laws -import cats.{Apply, Id, Semigroup, Traverse1} +import cats.{Apply, Id, Semigroup, NonEmptyTraverse} import cats.data.{Const, Nested} -import cats.syntax.traverse1._ +import cats.syntax.nonEmptyTraverse._ import cats.syntax.reducible._ -trait Traverse1Laws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] { - implicit override def F: Traverse1[F] +trait NonEmptyTraverseLaws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] { + implicit override def F: NonEmptyTraverse[F] - def traverse1Identity[A, B](fa: F[A], f: A => B): IsEq[F[B]] = { - fa.traverse1[Id, B](f) <-> F.map(fa)(f) + def nonEmptyTraverseIdentity[A, B](fa: F[A], f: A => B): IsEq[F[B]] = { + fa.nonEmptyTraverse[Id, B](f) <-> F.map(fa)(f) } - def traverse1SequentialComposition[A, B, C, M[_], N[_]]( + def nonEmptyTraverseSequentialComposition[A, B, C, M[_], N[_]]( fa: F[A], f: A => M[B], g: B => N[C] @@ -22,12 +22,12 @@ trait Traverse1Laws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] { M: Apply[M] ): IsEq[Nested[M, N, F[C]]] = { - val lhs = Nested(M.map(fa.traverse1(f))(fb => fb.traverse1(g))) - val rhs = fa.traverse1[Nested[M, N, ?], C](a => Nested(M.map(f(a))(g))) + val lhs = Nested(M.map(fa.nonEmptyTraverse(f))(fb => fb.nonEmptyTraverse(g))) + val rhs = fa.nonEmptyTraverse[Nested[M, N, ?], C](a => Nested(M.map(f(a))(g))) lhs <-> rhs } - def traverse1ParallelComposition[A, B, M[_], N[_]]( + def nonEmptyTraverseParallelComposition[A, B, M[_], N[_]]( fa: F[A], f: A => M[B], g: A => N[B] @@ -52,8 +52,8 @@ trait Traverse1Laws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] { (M.product(mx, my), N.product(nx, ny)) } } - val lhs: MN[F[B]] = fa.traverse1[MN, B](a => (f(a), g(a))) - val rhs: MN[F[B]] = (fa.traverse1(f), fa.traverse1(g)) + val lhs: MN[F[B]] = fa.nonEmptyTraverse[MN, B](a => (f(a), g(a))) + val rhs: MN[F[B]] = (fa.nonEmptyTraverse(f), fa.nonEmptyTraverse(g)) lhs <-> rhs } @@ -61,13 +61,13 @@ trait Traverse1Laws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] { fa: F[A], f: A => B )(implicit B: Semigroup[B]): IsEq[B] = { - val lhs: B = fa.traverse1[Const[B, ?], B](a => Const(f(a))).getConst + val lhs: B = fa.nonEmptyTraverse[Const[B, ?], B](a => Const(f(a))).getConst val rhs: B = fa.reduceMap(f) lhs <-> rhs } } -object Traverse1Laws { - def apply[F[_]](implicit ev: Traverse1[F]): Traverse1Laws[F] = - new Traverse1Laws[F] { def F: Traverse1[F] = ev } +object NonEmptyTraverseLaws { + def apply[F[_]](implicit ev: NonEmptyTraverse[F]): NonEmptyTraverseLaws[F] = + new NonEmptyTraverseLaws[F] { def F: NonEmptyTraverse[F] = ev } } diff --git a/laws/src/main/scala/cats/laws/ReducibleLaws.scala b/laws/src/main/scala/cats/laws/ReducibleLaws.scala index bdfac81152..bd74e04e0d 100644 --- a/laws/src/main/scala/cats/laws/ReducibleLaws.scala +++ b/laws/src/main/scala/cats/laws/ReducibleLaws.scala @@ -39,10 +39,10 @@ trait ReducibleLaws[F[_]] extends FoldableLaws[F] { fa.reduce <-> fa.reduceLeft(B.combine) def traverseConsistent[G[_]: Applicative, A, B](fa: F[A], f: A => G[B]): IsEq[G[Unit]] = - fa.traverse1_(f) <-> fa.traverse_(f) + fa.nonEmptyTraverse_(f) <-> fa.traverse_(f) def sequenceConsistent[G[_]: Applicative, A](fa: F[G[A]]): IsEq[G[Unit]] = - fa.sequence1_ <-> fa.sequence_ + fa.nonEmptySequence_ <-> fa.sequence_ def sizeConsistent[A](fa: F[A]): IsEq[Long] = fa.size <-> fa.reduceMap(_ => 1L) diff --git a/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala b/laws/src/main/scala/cats/laws/discipline/NonEmptyTraverseTests.scala similarity index 52% rename from laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala rename to laws/src/main/scala/cats/laws/discipline/NonEmptyTraverseTests.scala index 7b37dc6bbe..d127b54ea9 100644 --- a/laws/src/main/scala/cats/laws/discipline/Traverse1Tests.scala +++ b/laws/src/main/scala/cats/laws/discipline/NonEmptyTraverseTests.scala @@ -3,14 +3,14 @@ package cats.laws.discipline import org.scalacheck.{Arbitrary, Cogen, Prop} import Prop.forAll -import cats.{Applicative, Eq, Monoid, Traverse1} -import cats.laws.Traverse1Laws +import cats.{Applicative, Eq, Monoid, NonEmptyTraverse} +import cats.laws.NonEmptyTraverseLaws -trait Traverse1Tests[F[_]] extends TraverseTests[F] with ReducibleTests[F] { - def laws: Traverse1Laws[F] +trait NonEmptyTraverseTests[F[_]] extends TraverseTests[F] with ReducibleTests[F] { + def laws: NonEmptyTraverseLaws[F] - def traverse1[G[_]: Applicative, A: Arbitrary, B: Arbitrary, C: Arbitrary, M: Arbitrary, X[_]: Applicative, Y[_]: Applicative](implicit + def nonEmptyTraverse[G[_]: Applicative, A: Arbitrary, B: Arbitrary, C: Arbitrary, M: Arbitrary, X[_]: Applicative, Y[_]: Applicative](implicit ArbFA: Arbitrary[F[A]], ArbXB: Arbitrary[X[B]], ArbYB: Arbitrary[Y[B]], @@ -40,20 +40,20 @@ trait Traverse1Tests[F[_]] extends TraverseTests[F] with ReducibleTests[F] { EqXFB.eqv(x._1, y._1) && EqYFB.eqv(x._2, y._2) } new RuleSet { - def name: String = "traverse1" + def name: String = "nonEmptyTraverse" def bases: Seq[(String, RuleSet)] = Nil def parents: Seq[RuleSet] = Seq(traverse[A, B, C, M, X, Y], reducible[G, A, B]) def props: Seq[(String, Prop)] = Seq( - "traverse1 identity" -> forAll(laws.traverse1Identity[A, C] _), - "traverse1 sequential composition" -> forAll(laws.traverse1SequentialComposition[A, B, C, X, Y] _), - "traverse1 parallel composition" -> forAll(laws.traverse1ParallelComposition[A, B, X, Y] _), - "traverse1 derive reduceMap" -> forAll(laws.reduceMapDerived[A, M] _) + "nonEmptyTraverse identity" -> forAll(laws.nonEmptyTraverseIdentity[A, C] _), + "nonEmptyTraverse sequential composition" -> forAll(laws.nonEmptyTraverseSequentialComposition[A, B, C, X, Y] _), + "nonEmptyTraverse parallel composition" -> forAll(laws.nonEmptyTraverseParallelComposition[A, B, X, Y] _), + "nonEmptyTraverse derive reduceMap" -> forAll(laws.reduceMapDerived[A, M] _) ) } } } -object Traverse1Tests { - def apply[F[_]: Traverse1]: Traverse1Tests[F] = - new Traverse1Tests[F] { def laws: Traverse1Laws[F] = Traverse1Laws[F] } +object NonEmptyTraverseTests { + def apply[F[_]: NonEmptyTraverse]: NonEmptyTraverseTests[F] = + new NonEmptyTraverseTests[F] { def laws: NonEmptyTraverseLaws[F] = NonEmptyTraverseLaws[F] } } diff --git a/laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala b/laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala index 216a567f45..91bc6371a1 100644 --- a/laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala @@ -35,8 +35,8 @@ trait ReducibleTests[F[_]] extends FoldableTests[F] { forAll(laws.reduceRightConsistentWithReduceRightOption[A] _), "reduce consistent with reduceLeft" -> forAll(laws.reduceReduceLeftConsistent[B] _), - "traverse1_ consistent with traverse_" -> forAll(laws.traverseConsistent[G, A, B] _), - "sequence1_ consistent with sequence_" -> forAll(laws.sequenceConsistent[G, A] _), + "nonEmptyTraverse_ consistent with traverse_" -> forAll(laws.traverseConsistent[G, A, B] _), + "nonEmptySequence_ consistent with sequence_" -> forAll(laws.sequenceConsistent[G, A] _), "size consistent with reduceMap" -> forAll(laws.sizeConsistent[A] _) ) } diff --git a/tests/src/test/scala/cats/tests/NestedTests.scala b/tests/src/test/scala/cats/tests/NestedTests.scala index fb8a624d48..70000c172d 100644 --- a/tests/src/test/scala/cats/tests/NestedTests.scala +++ b/tests/src/test/scala/cats/tests/NestedTests.scala @@ -131,9 +131,9 @@ class NestedTests extends CatsSuite { } { - //Traverse1 composition - checkAll("Nested[NonEmptyList, NonEmptyVector, ?]", Traverse1Tests[Nested[NonEmptyList, NonEmptyVector, ?]].traverse1[Option, Int, Int, Int, Int, Option, Option]) - checkAll("Traverse1[Nested[NonEmptyList, NonEmptyVector, ?]]", SerializableTests.serializable(Traverse1[Nested[NonEmptyList, NonEmptyVector, ?]])) + //NonEmptyTraverse composition + checkAll("Nested[NonEmptyList, NonEmptyVector, ?]", NonEmptyTraverseTests[Nested[NonEmptyList, NonEmptyVector, ?]].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyTraverse[Nested[NonEmptyList, NonEmptyVector, ?]]", SerializableTests.serializable(NonEmptyTraverse[Nested[NonEmptyList, NonEmptyVector, ?]])) } diff --git a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala index 72d77cc012..e2b360777e 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala @@ -4,7 +4,7 @@ package tests import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.data.NonEmptyList -import cats.laws.discipline.{ComonadTests, SemigroupKTests, MonadTests, SerializableTests, Traverse1Tests, ReducibleTests} +import cats.laws.discipline.{ComonadTests, SemigroupKTests, MonadTests, SerializableTests, NonEmptyTraverseTests, ReducibleTests} import cats.laws.discipline.arbitrary._ class NonEmptyListTests extends CatsSuite { @@ -14,8 +14,8 @@ class NonEmptyListTests extends CatsSuite { checkAll("NonEmptyList[Int]", OrderLaws[NonEmptyList[Int]].order) - checkAll("NonEmptyList[Int] with Option", Traverse1Tests[NonEmptyList].traverse1[Option, Int, Int, Int, Int, Option, Option]) - checkAll("Traverse1[NonEmptyList[A]]", SerializableTests.serializable(Traverse1[NonEmptyList])) + checkAll("NonEmptyList[Int] with Option", NonEmptyTraverseTests[NonEmptyList].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyTraverse[NonEmptyList[A]]", SerializableTests.serializable(NonEmptyTraverse[NonEmptyList])) checkAll("NonEmptyList[Int]", ReducibleTests[NonEmptyList].reducible[Option, Int, Int]) checkAll("Reducible[NonEmptyList]", SerializableTests.serializable(Reducible[NonEmptyList])) diff --git a/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala b/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala index 059fb3167c..65d0ecf9ce 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala @@ -6,7 +6,7 @@ import catalysts.Platform import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.data.NonEmptyVector -import cats.laws.discipline.{ComonadTests, SemigroupKTests, FoldableTests, SerializableTests, Traverse1Tests, ReducibleTests, MonadTests} +import cats.laws.discipline.{ComonadTests, SemigroupKTests, FoldableTests, SerializableTests, NonEmptyTraverseTests, ReducibleTests, MonadTests} import cats.laws.discipline.arbitrary._ import scala.util.Properties @@ -18,8 +18,8 @@ class NonEmptyVectorTests extends CatsSuite { checkAll("NonEmptyVector[Int]", OrderLaws[NonEmptyVector[Int]].eqv) - checkAll("NonEmptyVector[Int] with Option", Traverse1Tests[NonEmptyVector].traverse1[Option, Int, Int, Int, Int, Option, Option]) - checkAll("Traverse1[NonEmptyVector[A]]", SerializableTests.serializable(Traverse1[NonEmptyVector])) + checkAll("NonEmptyVector[Int] with Option", NonEmptyTraverseTests[NonEmptyVector].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyTraverse[NonEmptyVector[A]]", SerializableTests.serializable(NonEmptyTraverse[NonEmptyVector])) checkAll("NonEmptyVector[Int]", ReducibleTests[NonEmptyVector].reducible[Option, Int, Int]) checkAll("Reducible[NonEmptyVector]", SerializableTests.serializable(Reducible[NonEmptyVector])) diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index 8e09970705..8033592ccb 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -5,7 +5,7 @@ import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.instances.stream._ import cats.data.{NonEmptyStream, OneAnd} -import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, Traverse1Tests, ReducibleTests} +import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, NonEmptyTraverseTests, ReducibleTests} import cats.laws.discipline.arbitrary._ class OneAndTests extends CatsSuite { @@ -15,8 +15,8 @@ class OneAndTests extends CatsSuite { checkAll("OneAnd[Stream, Int]", OrderLaws[OneAnd[Stream, Int]].eqv) - checkAll("OneAnd[Stream, Int] with Option", Traverse1Tests[OneAnd[Stream, ?]].traverse1[Option, Int, Int, Int, Int, Option, Option]) - checkAll("Traverse1[OneAnd[Stream, A]]", SerializableTests.serializable(Traverse1[OneAnd[Stream, ?]])) + checkAll("OneAnd[Stream, Int] with Option", NonEmptyTraverseTests[OneAnd[Stream, ?]].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyTraverse[OneAnd[Stream, A]]", SerializableTests.serializable(NonEmptyTraverse[OneAnd[Stream, ?]])) { implicit val traverse = ListWrapper.traverse diff --git a/tests/src/test/scala/cats/tests/SyntaxTests.scala b/tests/src/test/scala/cats/tests/SyntaxTests.scala index 217f54ba23..71727c4e08 100644 --- a/tests/src/test/scala/cats/tests/SyntaxTests.scala +++ b/tests/src/test/scala/cats/tests/SyntaxTests.scala @@ -140,16 +140,16 @@ object SyntaxTests extends AllInstances with AllSyntax { } - def testTraverse1[F[_]: Traverse1: FlatMap, G[_]: Apply: SemigroupK, A: Semigroup, B, Z]: Unit = { + def testNonEmptyTraverse[F[_]: NonEmptyTraverse: FlatMap, G[_]: Apply: SemigroupK, A: Semigroup, B, Z]: Unit = { val fa = mock[F[A]] val f1 = mock[A => G[B]] - val gfb: G[F[B]] = fa.traverse1(f1) + val gfb: G[F[B]] = fa.nonEmptyTraverse(f1) val f2 = mock[A => G[F[B]]] - val gfb2: G[F[B]] = fa.flatTraverse1(f2) + val gfb2: G[F[B]] = fa.nonEmptyFlatTraverse(f2) val fga = mock[F[G[A]]] - val gunit: G[F[A]] = fga.sequence1 + val gunit: G[F[A]] = fga.nonEmptySequence } def testReducible[F[_]: Reducible, G[_]: Apply: SemigroupK, A: Semigroup, B, Z]: Unit = { @@ -177,9 +177,9 @@ object SyntaxTests extends AllInstances with AllSyntax { val lb: Eval[B] = fa.reduceRightTo(f4)(f6) val f7 = mock[A => G[B]] - val gu1: G[Unit] = fa.traverse1_(f7) + val gu1: G[Unit] = fa.nonEmptyTraverse_(f7) - val gu2: G[Unit] = fga.sequence1_ + val gu2: G[Unit] = fga.nonEmptySequence_ } def testFunctor[F[_]: Functor, A, B]: Unit = { From 50cc4c0cbdc92f7fe199a6126a4ca22138c02312 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Mon, 12 Jun 2017 20:02:49 +0200 Subject: [PATCH 28/28] Rename intercalate1 to nonEmptyIntercalate --- core/src/main/scala/cats/Reducible.scala | 6 +++--- tests/src/test/scala/cats/tests/ReducibleTests.scala | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/cats/Reducible.scala b/core/src/main/scala/cats/Reducible.scala index 8cd56d9341..348c037ca1 100644 --- a/core/src/main/scala/cats/Reducible.scala +++ b/core/src/main/scala/cats/Reducible.scala @@ -164,13 +164,13 @@ import simulacrum.typeclass * scala> import cats.implicits._ * scala> import cats.data.NonEmptyList * scala> val nel = NonEmptyList.of("a", "b", "c") - * scala> Reducible[NonEmptyList].intercalate1(nel, "-") + * scala> Reducible[NonEmptyList].nonEmptyIntercalate(nel, "-") * res0: String = a-b-c - * scala> Reducible[NonEmptyList].intercalate1(NonEmptyList.of("a"), "-") + * scala> Reducible[NonEmptyList].nonEmptyIntercalate(NonEmptyList.of("a"), "-") * res1: String = a * }}} */ - def intercalate1[A](fa: F[A], a: A)(implicit A: Semigroup[A]): A = + def nonEmptyIntercalate[A](fa: F[A], a: A)(implicit A: Semigroup[A]): A = toNonEmptyList(fa) match { case NonEmptyList(hd, Nil) => hd case NonEmptyList(hd, tl) => diff --git a/tests/src/test/scala/cats/tests/ReducibleTests.scala b/tests/src/test/scala/cats/tests/ReducibleTests.scala index 4331b09c41..8f6230a82e 100644 --- a/tests/src/test/scala/cats/tests/ReducibleTests.scala +++ b/tests/src/test/scala/cats/tests/ReducibleTests.scala @@ -90,9 +90,9 @@ abstract class ReducibleCheck[F[_]: Reducible](name: String)(implicit ArbFInt: A } } - test(s"Reducible[$name].intercalate1") { + test(s"Reducible[$name].nonEmptyIntercalate") { forAll { (fa: F[String], a: String) => - fa.intercalate1(a) === (fa.toList.mkString(a)) + fa.nonEmptyIntercalate(a) === (fa.toList.mkString(a)) } } }