Skip to content

Commit

Permalink
Merge pull request #4460 from satorg/add-oneand-orders
Browse files Browse the repository at this point in the history
`OneAnd`: add `PartialOrder` and `Order` instances
  • Loading branch information
satorg committed Jun 26, 2023
2 parents 13d8a9d + 9623b83 commit 7935833
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 4 deletions.
26 changes: 25 additions & 1 deletion core/src/main/scala/cats/data/OneAnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,14 @@ sealed abstract private[data] class OneAndInstances extends OneAndLowPriority0 {

}

implicit def catsDataEqForOneAnd[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] = _ === _
implicit def catsDataOrderForOneAnd[A, F[_]](implicit A: Order[A], FA: Order[F[A]]): Order[OneAnd[F, A]] =
new Order[OneAnd[F, A]] {
def compare(x: OneAnd[F, A], y: OneAnd[F, A]): Int =
A.compare(x.head, y.head) match {
case 0 => FA.compare(x.tail, y.tail)
case neq => neq
}
}

implicit def catsDataShowForOneAnd[A, F[_]](implicit A: Show[A], FA: Show[F[A]]): Show[OneAnd[F, A]] = _.show

Expand Down Expand Up @@ -268,6 +275,9 @@ sealed abstract private[data] class OneAndLowPriority1 extends OneAndLowPriority
}

sealed abstract private[data] class OneAndLowPriority0_5 extends OneAndLowPriority1 {

implicit def catsDataEqForOneAnd[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] = _ === _

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)
Expand All @@ -280,6 +290,20 @@ sealed abstract private[data] class OneAndLowPriority0_5 extends OneAndLowPriori
}

sealed abstract private[data] class OneAndLowPriority0 extends OneAndLowPriority0_5 {

implicit def catsDataPartialOrderForOneAnd[A, F[_]](implicit
A: PartialOrder[A],
FA: PartialOrder[F[A]]
): PartialOrder[OneAnd[F, A]] =
new PartialOrder[OneAnd[F, A]] {
def partialCompare(x: OneAnd[F, A], y: OneAnd[F, A]): Double = {
A.partialCompare(x.head, y.head) match {
case 0.0 => FA.partialCompare(x.tail, y.tail)
case neq => neq
}
}
}

implicit def catsDataNonEmptyTraverseForOneAnd[F[_]](implicit
F: Traverse[F],
F2: Alternative[F]
Expand Down
59 changes: 56 additions & 3 deletions tests/shared/src/test/scala/cats/tests/OneAndSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,25 @@

package cats.tests

import cats.{Alternative, Applicative, Foldable, Functor, Monad, SemigroupK, Traverse}
import cats.Alternative
import cats.Applicative
import cats.Foldable
import cats.Functor
import cats.Monad
import cats.SemigroupK
import cats.Traverse
import cats.data.OneAnd
import cats.kernel.Eq
import cats.kernel.Order
import cats.kernel.PartialOrder
import cats.kernel.laws.discipline.EqTests
import cats.kernel.laws.discipline.OrderTests
import cats.kernel.laws.discipline.PartialOrderTests
import cats.laws.discipline.SemigroupalTests.Isomorphisms
import cats.laws.discipline._
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.SemigroupalTests.Isomorphisms
import cats.syntax.foldable._
import cats.syntax.eq._
import cats.syntax.order._
import org.scalacheck.Prop._
import org.scalacheck.Test.Parameters

Expand All @@ -36,6 +48,47 @@ class OneAndSuite extends CatsSuite {
implicit override val scalaCheckTestParameters: Parameters =
Parameters.default.withMinSuccessfulTests(20).withMaxSize(Parameters.default.minSize + 5)

// Test kernel instances
{
import Helpers.Eqed

checkAll("OneAnd[F, A]", EqTests[OneAnd[ListWrapper, Eqed]].eqv)
checkAll("Eq[OneAnd[F, A]]", SerializableTests.serializable(Eq[OneAnd[ListWrapper, Eqed]]))

property("Eq[OneAnd[F, A]]: must be consistent with Eq[Tuple2[A, F[A]]]") {
forAll { (x: OneAnd[ListWrapper, Eqed], y: OneAnd[ListWrapper, Eqed]) =>
assertEquals(x.eqv(y), (x.head, x.tail).eqv((y.head, y.tail)))
}
}
}
{
import Helpers.POrd

implicit val partialOrder: PartialOrder[ListWrapper[POrd]] = ListWrapper.partialOrder
checkAll("OneAnd[F, A]", PartialOrderTests[OneAnd[ListWrapper, POrd]].partialOrder)
checkAll("PartialOrder[OneAnd[F, A]]", SerializableTests.serializable(PartialOrder[OneAnd[ListWrapper, POrd]]))

property("PartialOrder[OneAnd[F, A]]: must be consistent with PartialOrder[Tuple2[A, F[A]]]") {
forAll { (x: OneAnd[ListWrapper, POrd], y: OneAnd[ListWrapper, POrd]) =>
// `NaN` cannot be compared directly; hence using `partialComparison` instead of `partialCompare`.
assertEquals(x.partialComparison(y), (x.head, x.tail).partialComparison((y.head, y.tail)))
}
}
}
{
import Helpers.Ord

implicit val order: Order[ListWrapper[Ord]] = ListWrapper.order
checkAll("OneAnd[F, A]", OrderTests[OneAnd[ListWrapper, Ord]].order)
checkAll("Order[OneAnd[F, A]]", SerializableTests.serializable(Order[OneAnd[ListWrapper, Ord]]))

property("Order[OneAnd[F, A]]: must be consistent with Order[Tuple2[A, F[A]]]") {
forAll { (x: OneAnd[ListWrapper, Ord], y: OneAnd[ListWrapper, Ord]) =>
assertEquals(x.compare(y), (x.head, x.tail).compare((y.head, y.tail)))
}
}
}

{
implicit val traverse: Traverse[OneAnd[ListWrapper, *]] = OneAnd.catsDataTraverseForOneAnd(ListWrapper.traverse)
checkAll("OneAnd[ListWrapper, Int] with Option",
Expand Down

0 comments on commit 7935833

Please sign in to comment.