Skip to content

Commit

Permalink
Port partial order to new scheme (#480)
Browse files Browse the repository at this point in the history
* Port invariant derivation to new scheme

* Port scala 2 invariant tests to scala 3

* Consistent test naming

* WIP extra Invariant instances

* Tests for invariant derivation syntax

* Rename partial order derivation

* Port partial order derivation to new scheme

* Rename to PartialOrderSuite

* Port scala 2 partial order suite to scala 3

Co-authored-by: Georgi Krastev <[email protected]>
  • Loading branch information
TimWSpence and joroKr21 authored May 30, 2022
1 parent 5de791e commit 21c41a3
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 45 deletions.
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.

0 comments on commit 21c41a3

Please sign in to comment.