Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port partial order to new scheme #480

Merged
merged 11 commits into from
May 30, 2022
46 changes: 46 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedPartialOrder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cats.derived

import cats.{PartialOrder, Show}
import shapeless3.deriving.{Complete, Continue, K0, Labelling}

import scala.annotation.implicitNotFound
import scala.compiletime.*
import scala.deriving.Mirror

@implicitNotFound("""Could not derive an instance of PartialOrder[A] where A = ${A}.
Make sure that A satisfies one of the following conditions:
* it is a case class where all fields have a PartialOrder instance
* it is a sealed trait where all subclasses have a PartialOrder instance""")
type DerivedPartialOrder[A] = Derived[PartialOrder[A]]
object DerivedPartialOrder:
type Or[A] = Derived.Or[PartialOrder[A]]

inline def apply[A]: PartialOrder[A] =
import DerivedPartialOrder.given
summonInline[DerivedPartialOrder[A]].instance

given product[A](using inst: => K0.ProductInstances[Or, A]): DerivedPartialOrder[A] =
given K0.ProductInstances[PartialOrder, A] = inst.unify
new Product[PartialOrder, A] {}

given coproduct[A](using inst: => K0.CoproductInstances[Or, A]): DerivedPartialOrder[A] =
given K0.CoproductInstances[PartialOrder, A] = inst.unify
new Coproduct[PartialOrder, A] {}

trait Product[T[x] <: PartialOrder[x], A](using inst: K0.ProductInstances[T, A]) extends PartialOrder[A]:

def partialCompare(x: A, y: A): Double =
inst.foldLeft2(x, y)(0: Double)(
[t] =>
(acc: Double, ord: T[t], t0: t, t1: t) => {
val cmp = ord.partialCompare(t0, t1)
Complete(cmp != 0)(cmp)(acc)
}
)

trait Coproduct[T[x] <: PartialOrder[x], A](using inst: K0.CoproductInstances[T, A]) extends PartialOrder[A]:

def partialCompare(x: A, y: A): Double =
inst.fold2(x, y)(Double.NaN: Double)(
[t] => (ord: T[t], t0: t, t1: t) => ord.partialCompare(t0, t1)
)
8 changes: 6 additions & 2 deletions core/src/main/scala-3/cats/derived/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ extension (x: SemigroupK.type) inline def derived[F[_]]: SemigroupK[F] = Derived
extension (x: MonoidK.type) inline def derived[F[_]]: MonoidK[F] = DerivedMonoidK[F]
extension (x: Contravariant.type) inline def derived[F[_]]: Contravariant[F] = DerivedContravariant[F]
extension (x: Invariant.type) inline def derived[F[_]]: Invariant[F] = DerivedInvariant[F]
extension (x: PartialOrder.type) inline def derived[A]: PartialOrder[A] = DerivedPartialOrder[A]

object semiauto extends PartialOrderDerivation, Instances:

object semiauto extends Instances:
inline def eq[A]: Eq[A] = DerivedEq[A]
inline def hash[A]: Hash[A] = DerivedHash[A]
inline def empty[A]: Empty[A] = DerivedEmpty[A]
Expand All @@ -53,6 +53,7 @@ object semiauto extends PartialOrderDerivation, Instances:
inline def monoidK[F[_]]: MonoidK[F] = DerivedMonoidK[F]
inline def contravariant[F[_]]: Contravariant[F] = DerivedContravariant[F]
inline def invariant[F[_]]: Invariant[F] = DerivedInvariant[F]
inline def partialOrder[A]: PartialOrder[A] = DerivedPartialOrder[A]

object auto:
object eq:
Expand Down Expand Up @@ -120,3 +121,6 @@ object auto:

object invariant:
inline given [F[_]](using NotGiven[Invariant[F]]): Invariant[F] = DerivedInvariant[F]

object partialOrder:
inline given [A](using NotGiven[PartialOrder[A]]): PartialOrder[A] = DerivedPartialOrder[A]
34 changes: 0 additions & 34 deletions core/src/main/scala-3/cats/derived/partialOrder.scala

This file was deleted.

79 changes: 79 additions & 0 deletions core/src/test/scala-3/cats/derived/PartialOrderSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2015 Miles Sabin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cats
package derived
import cats.kernel.laws.discipline.{PartialOrderTests, SerializableTests}
import org.scalacheck.{Arbitrary, Cogen}
import scala.compiletime.*

class PartialOrderSuite extends KittensSuite:
import PartialOrderSuite.*
import TestDefns.*

inline def partialOrderTests[A]: PartialOrderTests[A] = PartialOrderTests[A](summonInline)

inline def testPartialOrder(context: String): Unit =
checkAll(s"$context.PartialOrder[IList[Int]]", partialOrderTests[IList[Int]].partialOrder)
checkAll(s"$context.PartialOrder[Inner]", partialOrderTests[Inner].partialOrder)
checkAll(s"$context.PartialOrder[Outer]", partialOrderTests[Outer].partialOrder)
checkAll(s"$context.PartialOrder[Interleaved[Int]]", partialOrderTests[Interleaved[Int]].partialOrder)
checkAll(s"$context.PartialOrder[Tree[Int]]", partialOrderTests[Tree[Int]].partialOrder)
checkAll(s"$context.PartialOrder[Recursive]", partialOrderTests[Recursive].partialOrder)
checkAll(s"$context.PartialOrder[Box[KeyValue]]", partialOrderTests[Box[KeyValue]].partialOrder)
checkAll(
s"$context.PartialOrder is Serialiable",
SerializableTests.serializable(summonInline[PartialOrder[Tree[Int]]])
)

test(s"$context.PartialOrder respects existing instances") {
val boxKeyValue = summonInline[PartialOrder[Box[KeyValue]]]
val x = Box(KeyValue("red", 1))
val y = Box(KeyValue("red", 2))
val z = Box(KeyValue("blue", 1))
assert(boxKeyValue.partialCompare(x, y) < 0)
assert(boxKeyValue.partialCompare(y, z).isNaN)
}

locally {
import auto.partialOrder.given
testPartialOrder("auto")
}

locally {
import semiInstances.given
testPartialOrder("semiauto")
}

object PartialOrderSuite:
import TestDefns.*

object semiInstances:
implicit val iList: PartialOrder[IList[Int]] = semiauto.partialOrder
implicit val inner: PartialOrder[Inner] = semiauto.partialOrder
implicit val outer: PartialOrder[Outer] = semiauto.partialOrder
implicit val interleaved: PartialOrder[Interleaved[Int]] = semiauto.partialOrder
implicit val tree: PartialOrder[Tree[Int]] = semiauto.partialOrder
implicit val recursive: PartialOrder[Recursive] = semiauto.partialOrder
implicit val boxKeyValue: PartialOrder[Box[KeyValue]] = semiauto.partialOrder

final case class KeyValue(key: String, value: Int)
object KeyValue extends ((String, Int) => KeyValue):
implicit val arbitrary: Arbitrary[KeyValue] = Arbitrary(Arbitrary.arbitrary[(String, Int)].map(tupled))
implicit val cogen: Cogen[KeyValue] = Cogen[(String, Int)].contramap(kv => kv.key -> kv.value)

implicit val partialOrder: PartialOrder[KeyValue] =
PartialOrder.from((x, y) => if (x.key == y.key) x.value.toDouble - y.value.toDouble else Double.NaN)
9 changes: 0 additions & 9 deletions core/src/test/scala-3/cats/derived/PartialOrderTests.scala

This file was deleted.