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

Add Defer instances for Eq, Hash, Order, Show, and variants #4414

Merged
merged 10 commits into from
Jul 10, 2023
1 change: 1 addition & 0 deletions core/src/main/scala-2.12/cats/instances/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ trait AllInstances
with PartialOrderingInstances
with QueueInstances
with SetInstances
with ShowInstances
with SortedMapInstances
with SortedSetInstances
with StreamInstances
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala-2.12/cats/instances/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ package object instances {
object set extends SetInstances
object seq extends SeqInstances
object short extends ShortInstances
object show extends ShowInstances
object sortedMap
extends SortedMapInstances
with SortedMapInstancesBinCompat0
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala-2.13+/cats/instances/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ trait AllInstances
with SetInstances
with SortedMapInstances
with SortedSetInstances
with ShowInstances
with StreamInstances
with StringInstances
with SymbolInstances
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala-2.13+/cats/instances/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ package object instances {
object set extends SetInstances
object seq extends SeqInstances
object short extends ShortInstances
object show extends ShowInstances
object sortedMap
extends SortedMapInstances
with SortedMapInstancesBinCompat0
Expand Down
9 changes: 9 additions & 0 deletions core/src/main/scala/cats/Defer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,16 @@ trait Defer[F[_]] extends Serializable {
object Defer {
def apply[F[_]](implicit defer: Defer[F]): Defer[F] = defer

implicit def catsDeferForEq: Defer[Eq] = cats.implicits.catsDeferForEq
implicit def catsDeferForEquiv: Defer[Equiv] = cats.implicits.catsDeferForEquiv
implicit def catsDeferForFunction0: Defer[Function0] = cats.instances.function.catsSddDeferForFunction0
implicit def catsDeferForFunction1[A]: Defer[Function1[A, *]] = cats.instances.function.catsStdDeferForFunction1[A]
implicit def catsDeferForHash: Defer[Hash] = cats.implicits.catsDeferForHash
implicit def catsDeferForOrder: Defer[Order] = cats.instances.order.catsDeferForOrder
implicit def catsStdDeferForOrdering: Defer[Ordering] = cats.instances.ordering.catsStdDeferForOrdering
implicit def catsDeferForPartialOrder: Defer[PartialOrder] = cats.instances.partialOrder.catsDeferForPartialOrder
implicit def catsStdDeferForPartialOrdering: Defer[PartialOrdering] =
cats.instances.partialOrdering.catsStdDeferForPartialOrdering
implicit def catsDeferForShow: Defer[Show] = cats.implicits.catsDeferForShow
implicit def catsDeferForTailRec: Defer[TailRec] = cats.instances.tailRec.catsInstancesForTailRec
}
27 changes: 27 additions & 0 deletions core/src/main/scala/cats/instances/eq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
package cats
package instances

import scala.annotation.tailrec

trait EqInstances extends kernel.instances.EqInstances {
implicit val catsContravariantMonoidalForEq: ContravariantMonoidal[Eq] =
new ContravariantMonoidal[Eq] {
Expand All @@ -43,4 +45,29 @@ trait EqInstances extends kernel.instances.EqInstances {
def product[A, B](fa: Eq[A], fb: Eq[B]): Eq[(A, B)] =
(left, right) => fa.eqv(left._1, right._1) && fb.eqv(left._2, right._2)
}

implicit def catsDeferForEq: Defer[Eq] = EqInstances.catsDeferForEqCache
}
object EqInstances {
private val catsDeferForEqCache: Defer[Eq] =
new Defer[Eq] {
case class Deferred[A](fa: () => Eq[A]) extends Eq[A] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a stylish nitpick that perhaps doesn't affect anything. I'm not confident about putting underlying classes to the value instantiation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't usually use this style, and went with it to remain consistent with the existing instances (like cats.instances.FunctionInstancesBinCompat0#catsSddDeferForFunction0).

As I usually avoid it for stylistic reasons, I'm not familiar with the practical concerns around doing it this way, so I'd be interested in hearing why you're worried about it.

private lazy val resolved: Eq[A] = {
morgen-peschke marked this conversation as resolved.
Show resolved Hide resolved
@tailrec
def loop(f: () => Eq[A]): Eq[A] =
f() match {
case Deferred(f) => loop(f)
case next => next
}

loop(fa)
}
override def eqv(x: A, y: A): Boolean = resolved.eqv(x, y)
}

override def defer[A](fa: => Eq[A]): Eq[A] = {
lazy val cachedFa = fa
Deferred(() => cachedFa)
}
}
}
27 changes: 27 additions & 0 deletions core/src/main/scala/cats/instances/equiv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
package cats
package instances

import scala.annotation.tailrec

trait EquivInstances {
implicit val catsContravariantMonoidalForEquiv: ContravariantMonoidal[Equiv] =
new ContravariantMonoidal[Equiv] {
Expand Down Expand Up @@ -52,4 +54,29 @@ trait EquivInstances {
fa.equiv(l._1, r._1) && fb.equiv(l._2, r._2)
}
}

implicit def catsDeferForEquiv: Defer[Equiv] = EquivInstances.catsDeferForEquivCache
}
object EquivInstances {
private val catsDeferForEquivCache: Defer[Equiv] =
new Defer[Equiv] {
case class Deferred[A](fa: () => Equiv[A]) extends Equiv[A] {
private lazy val resolved: Equiv[A] = {
@tailrec
def loop(f: () => Equiv[A]): Equiv[A] =
f() match {
case Deferred(f) => loop(f)
case next => next
}

loop(fa)
}
override def equiv(x: A, y: A): Boolean = resolved.equiv(x, y)
}

override def defer[A](fa: => Equiv[A]): Equiv[A] = {
lazy val cachedFa = fa
Deferred(() => cachedFa)
}
}
}
16 changes: 10 additions & 6 deletions core/src/main/scala/cats/instances/function.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,17 @@ private[instances] trait FunctionInstancesBinCompat0 {
implicit val catsSddDeferForFunction0: Defer[Function0] =
new Defer[Function0] {
case class Deferred[A](fa: () => Function0[A]) extends Function0[A] {
def apply() = {
private lazy val resolved: Function0[A] = {
@annotation.tailrec
def loop(f: () => Function0[A]): A =
def loop(f: () => Function0[A]): Function0[A] =
f() match {
case Deferred(f) => loop(f)
case next => next()
case next => next
}

loop(fa)
}
def apply(): A = resolved()
}
def defer[A](fa: => Function0[A]): Function0[A] = {
lazy val cachedFa = fa
Expand All @@ -65,15 +67,17 @@ private[instances] trait FunctionInstancesBinCompat0 {
implicit def catsStdDeferForFunction1[A]: Defer[A => *] =
new Defer[A => *] {
case class Deferred[B](fa: () => A => B) extends (A => B) {
def apply(a: A) = {
private lazy val resolved: A => B = {
@annotation.tailrec
def loop(f: () => A => B): B =
def loop(f: () => A => B): A => B =
f() match {
case Deferred(f) => loop(f)
case next => next(a)
case next => next
}

loop(fa)
}
def apply(a: A): B = resolved(a)
}
def defer[B](fa: => A => B): A => B = {
lazy val cachedFa = fa
Expand Down
32 changes: 32 additions & 0 deletions core/src/main/scala/cats/instances/hash.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
package cats
package instances

import scala.annotation.tailrec

trait HashInstances extends kernel.instances.HashInstances {

implicit val catsContravariantForHash: Contravariant[Hash] =
Expand All @@ -34,4 +36,34 @@ trait HashInstances extends kernel.instances.HashInstances {

}

implicit def catsDeferForHash: Defer[Hash] = HashInstances.catsDeferForHashCache
}
object HashInstances {
private val catsDeferForHashCache: Defer[Hash] =
new Defer[Hash] {
case class Deferred[A](fa: () => Hash[A]) extends Hash[A] {
private lazy val resolve: Hash[A] = {
@tailrec
def loop(f: () => Hash[A]): Hash[A] =
f() match {
case Deferred(f) => loop(f)
case next => next
}

loop(fa)
}

override def hash(x: A): Int = resolve.hash(x)

/**
* Returns `true` if `x` and `y` are equivalent, `false` otherwise.
*/
override def eqv(x: A, y: A): Boolean = resolve.eqv(x, y)
}

override def defer[A](fa: => Hash[A]): Hash[A] = {
lazy val cachedFa = fa
Deferred(() => cachedFa)
}
}
}
27 changes: 27 additions & 0 deletions core/src/main/scala/cats/instances/order.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ package instances

import cats.kernel.instances.unit._

import scala.annotation.tailrec

trait OrderInstances extends kernel.instances.OrderInstances {

implicit val catsContravariantMonoidalForOrder: ContravariantMonoidal[Order] =
Expand All @@ -47,4 +49,29 @@ trait OrderInstances extends kernel.instances.OrderInstances {
if (z == 0) fb.compare(x._2, y._2) else z
}
}

implicit def catsDeferForOrder: Defer[Order] = OrderInstances.catsDeferForOrderCache
}
object OrderInstances {
private val catsDeferForOrderCache: Defer[Order] =
new Defer[Order] {
case class Deferred[A](fa: () => Order[A]) extends Order[A] {
private lazy val resolved: Order[A] = {
@tailrec
def loop(f: () => Order[A]): Order[A] =
f() match {
case Deferred(f) => loop(f)
case next => next
}

loop(fa)
}
override def compare(x: A, y: A): Int = resolved.compare(x, y)
}

override def defer[A](fa: => Order[A]): Order[A] = {
lazy val cachedFa = fa
Deferred(() => cachedFa)
}
}
}
27 changes: 27 additions & 0 deletions core/src/main/scala/cats/instances/ordering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ package instances

import cats.kernel.instances.unit._

import scala.annotation.tailrec

trait OrderingInstances {
implicit val catsContravariantMonoidalForOrdering: ContravariantMonoidal[Ordering] =
new ContravariantMonoidal[Ordering] {
Expand All @@ -43,4 +45,29 @@ trait OrderingInstances {
}
}
}

implicit def catsStdDeferForOrdering: Defer[Ordering] = OrderingInstances.catsStdDeferForOrderingCache
}
object OrderingInstances {
private val catsStdDeferForOrderingCache: Defer[Ordering] =
new Defer[Ordering] {
case class Deferred[A](fa: () => Ordering[A]) extends Ordering[A] {
private lazy val resolved: Ordering[A] = {
@tailrec
def loop(f: () => Ordering[A]): Ordering[A] =
f() match {
case Deferred(f) => loop(f)
case next => next
}

loop(fa)
}
override def compare(x: A, y: A): Int = resolved.compare(x, y)
}

override def defer[A](fa: => Ordering[A]): Ordering[A] = {
lazy val cachedFa = fa
Deferred(() => cachedFa)
}
}
}
27 changes: 27 additions & 0 deletions core/src/main/scala/cats/instances/partialOrder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ package cats
package instances
import cats.kernel.instances.unit._

import scala.annotation.tailrec

trait PartialOrderInstances extends kernel.instances.PartialOrderInstances {
implicit val catsContravariantMonoidalForPartialOrder: ContravariantMonoidal[PartialOrder] =
new ContravariantMonoidal[PartialOrder] {
Expand All @@ -41,4 +43,29 @@ trait PartialOrderInstances extends kernel.instances.PartialOrderInstances {

def unit: PartialOrder[Unit] = Order[Unit]
}

implicit def catsDeferForPartialOrder: Defer[PartialOrder] = PartialOrderInstances.catsDeferForPartialOrderCache
}
object PartialOrderInstances {
private val catsDeferForPartialOrderCache: Defer[PartialOrder] =
new Defer[PartialOrder] {
case class Deferred[A](fa: () => PartialOrder[A]) extends PartialOrder[A] {
private lazy val resolved: PartialOrder[A] = {
@tailrec
def loop(f: () => PartialOrder[A]): PartialOrder[A] =
f() match {
case Deferred(f) => loop(f)
case next => next
}

loop(fa)
}
override def partialCompare(x: A, y: A): Double = resolved.partialCompare(x, y)
}

override def defer[A](fa: => PartialOrder[A]): PartialOrder[A] = {
lazy val cachedFa = fa
Deferred(() => cachedFa)
}
}
}
31 changes: 31 additions & 0 deletions core/src/main/scala/cats/instances/partialOrdering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
package cats
package instances

import scala.annotation.tailrec

trait PartialOrderingInstances {
implicit val catsContravariantMonoidalForPartialOrdering: ContravariantMonoidal[PartialOrdering] =
new ContravariantMonoidal[PartialOrdering] {
Expand Down Expand Up @@ -50,4 +52,33 @@ trait PartialOrderingInstances {

def unit: PartialOrdering[Unit] = cats.instances.unit.catsKernelStdOrderForUnit.toOrdering
}

implicit def catsStdDeferForPartialOrdering: Defer[PartialOrdering] =
PartialOrderingInstances.catsStdDeferForPartialOrderingCache
}
object PartialOrderingInstances {
private val catsStdDeferForPartialOrderingCache: Defer[PartialOrdering] =
new Defer[PartialOrdering] {
case class Deferred[A](fa: () => PartialOrdering[A]) extends PartialOrdering[A] {
private lazy val resolve: PartialOrdering[A] = {
@tailrec
def loop(f: () => PartialOrdering[A]): PartialOrdering[A] =
f() match {
case Deferred(f) => loop(f)
case next => next
}

loop(fa)
}

override def tryCompare(x: A, y: A): Option[Int] = resolve.tryCompare(x, y)

override def lteq(x: A, y: A): Boolean = resolve.lteq(x, y)
}

override def defer[A](fa: => PartialOrdering[A]): PartialOrdering[A] = {
lazy val cachedFa = fa
Deferred(() => cachedFa)
}
}
}
Loading