diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 5500d15548..ab76417dae 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -87,7 +87,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 OneAndLowPriority1 { +private[data] sealed trait OneAndInstances extends OneAndLowPriority2 { implicit def oneAndEq[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] = new Eq[OneAnd[F, A]]{ @@ -163,4 +163,23 @@ trait OneAndLowPriority1 extends OneAndLowPriority0 { } } +trait OneAndLowPriority2 extends OneAndLowPriority1 { + implicit def oneAndTraverse[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]] = { + val tail = F.traverse(fa.tail)(f) + val head = f(fa.head) + G.ap2(head, tail)(G.pure(OneAnd(_, _))) + } + + 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 diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index 0b6f5c3ea0..6728f9440f 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -4,7 +4,7 @@ package tests import algebra.laws.{GroupLaws, OrderLaws} import cats.data.{NonEmptyList, OneAnd} -import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, MonoidalTests} +import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, MonoidalTests, TraverseTests} import cats.laws.discipline.arbitrary.{evalArbitrary, oneAndArbitrary} import cats.laws.discipline.eq._ @@ -13,6 +13,9 @@ import scala.util.Random class OneAndTests extends CatsSuite { checkAll("OneAnd[List, Int]", OrderLaws[OneAnd[List, Int]].eqv) + checkAll("OneAnd[List, Int] with Option", TraverseTests[OneAnd[List, ?]].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("Traverse[OneAnd[List, A]]", SerializableTests.serializable(Traverse[OneAnd[List, ?]])) + implicit val iso = MonoidalTests.Isomorphisms.invariant[OneAnd[ListWrapper, ?]](OneAnd.oneAndFunctor(ListWrapper.functor)) // Test instances that have more general constraints