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

Make nested instances lazy #627

Merged
merged 2 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion core/src/main/scala-3/cats/derived/Derived.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package cats.derived

import shapeless3.deriving.*

import scala.annotation.*
import scala.compiletime.*
import scala.compiletime.summonFrom

@implicitNotFound("Could not derive an instance of ${A}")
opaque type Derived[A] = A
Expand All @@ -29,6 +30,9 @@ object Derived:
extension [I[f[_[_, _]], t[_, _]] <: K2.Instances[f, t], F[_[_, _]], T[_, _]](inst: I[Or2[F], T])
@targetName("unifyK2") def unify: I[F, T] = inst

abstract private[derived] class Lazy[A](f: () => A) extends Serializable:
final protected lazy val delegate: A = f()

sealed abstract class OrInstances:
inline given [A]: Derived.Or[A] = summonFrom {
case instance: A => Derived.Or(instance)
Expand Down
23 changes: 12 additions & 11 deletions core/src/main/scala-3/cats/derived/DerivedApplicative.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package cats.derived

import shapeless3.deriving.{Const, K1}
import cats.{Applicative, Monoid}

import scala.compiletime.*
import shapeless3.deriving.{Continue, K0, Labelling}
import shapeless3.deriving.{Const, K1}

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

@implicitNotFound("""Could not derive an instance of Applicative[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
Expand All @@ -23,17 +20,21 @@ object DerivedApplicative:
summonInline[DerivedApplicative[F]].instance

given [T](using T: Monoid[T]): DerivedApplicative[Const[T]] = new Applicative[Const[T]]:
def pure[A](x: A): Const[T][A] = T.empty
def ap[A, B](ff: T)(fa: T): Const[T][B] = T.combine(ff, fa)
def pure[A](x: A): T = T.empty
def ap[A, B](ff: T)(fa: T): T = T.combine(ff, fa)

given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedApplicative[[x] =>> F[G[x]]] =
F.unify.compose(G.unify)
given nested[F[_], G[_]](using F: => Or[F], G: => Or[G]): DerivedApplicative[[x] =>> F[G[x]]] =
new Derived.Lazy(() => F.unify.compose(G.unify)) with Applicative[[x] =>> F[G[x]]]:
export delegate.*

given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedApplicative[F] =
given K1.ProductInstances[Applicative, F] = inst.unify
new Product[Applicative, F] with DerivedApply.Product[Applicative, F] {}

trait Product[T[x[_]] <: Applicative[x], F[_]](using inst: K1.ProductInstances[T, F])
@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedApplicative_F[F[_]: Or, G[_]: Or]: DerivedApplicative[[x] =>> F[G[x]]] = summon

trait Product[T[f[_]] <: Applicative[f], F[_]](using inst: K1.ProductInstances[T, F])
extends Applicative[F],
DerivedApply.Product[T, F]:
override def pure[A](x: A): F[A] = inst.construct([t[_]] => (apl: T[t]) => apl.pure[A](x))
final override def pure[A](x: A): F[A] = inst.construct([f[_]] => (F: T[f]) => F.pure[A](x))
29 changes: 15 additions & 14 deletions core/src/main/scala-3/cats/derived/DerivedApply.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package cats.derived

import shapeless3.deriving.{Const, K1}
import cats.{Apply, Semigroup}

import scala.compiletime.*
import shapeless3.deriving.{Continue, K0, Labelling}
import shapeless3.deriving.{Const, K1}

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

@implicitNotFound("""Could not derive an instance of Apply[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
Expand All @@ -23,18 +20,22 @@ object DerivedApply:
summonInline[DerivedApply[F]].instance

given [T](using T: Semigroup[T]): DerivedApply[Const[T]] = new Apply[Const[T]]:
def ap[A, B](ff: T)(fa: T) = T.combine(ff, fa)
def map[A, B](fa: T)(f: A => B) = fa
def ap[A, B](ff: T)(fa: T): T = T.combine(ff, fa)
def map[A, B](fa: T)(f: A => B): T = fa

given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedApply[[x] =>> F[G[x]]] =
F.unify.compose(G.unify)
given nested[F[_], G[_]](using F: => Or[F], G: => Or[G]): DerivedApply[[x] =>> F[G[x]]] =
new Derived.Lazy(() => F.unify.compose(G.unify)) with Apply[[x] =>> F[G[x]]]:
export delegate.*

given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedApply[F] =
given K1.ProductInstances[Apply, F] = inst.unify
new Product[Apply, F] {}

trait Product[T[x[_]] <: Apply[x], F[_]](using inst: K1.ProductInstances[T, F]) extends Apply[F]:
override def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] =
inst.map2(ff, fa)([t[_]] => (apl: T[t], tt: t[A => B], ta: t[A]) => apl.ap(tt)(ta))
override def map[A, B](fa: F[A])(f: A => B): F[B] =
inst.map(fa: F[A])([f[_]] => (tf: T[f], fa: f[A]) => tf.map(fa)(f))
@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedApply_F[F[_]: Or, G[_]: Or]: DerivedApply[[x] =>> F[G[x]]] = summon

trait Product[T[f[_]] <: Apply[f], F[_]](using inst: K1.ProductInstances[T, F]) extends Apply[F]:
private lazy val F = new DerivedFunctor.Generic[T, F] {}
final override def map[A, B](fa: F[A])(f: A => B): F[B] = F.map(fa)(f)
final override def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] =
inst.map2(ff, fa)([f[_]] => (F: T[f], ff: f[A => B], fa: f[A]) => F.ap(ff)(fa))
16 changes: 10 additions & 6 deletions core/src/main/scala-3/cats/derived/DerivedContravariant.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cats.derived

import cats.{Contravariant, Functor}
import cats.Contravariant
import shapeless3.deriving.{Const, K1}

import scala.annotation.implicitNotFound
Expand All @@ -22,14 +22,18 @@ object DerivedContravariant:
given [T]: DerivedContravariant[Const[T]] = new Contravariant[Const[T]]:
def contramap[A, B](fa: T)(f: B => A): T = fa

given [F[_], G[_]](using F: DerivedFunctor.Or[F], G: Or[G]): DerivedContravariant[[x] =>> F[G[x]]] =
given Contravariant[G] = G.unify
F.unify.composeContravariant[G]
given nested[F[_], G[_]](using F: DerivedFunctor.Or[F], G: => Or[G]): DerivedContravariant[[x] =>> F[G[x]]] =
new Derived.Lazy(() => F.unify.composeContravariant(G.unify)) with Contravariant[[x] =>> F[G[x]]]:
export delegate.*

given [F[_]](using inst: => K1.Instances[Or, F]): DerivedContravariant[F] =
given K1.Instances[Contravariant, F] = inst.unify
new Generic[Contravariant, F] {}

trait Generic[T[x[_]] <: Contravariant[x], F[_]](using inst: K1.Instances[T, F]) extends Contravariant[F]:
@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedContravariant_F[F[_]: DerivedFunctor.Or, G[_]: Or]
: DerivedContravariant[[x] =>> F[G[x]]] = summon

trait Generic[T[f[_]] <: Contravariant[f], F[_]](using inst: K1.Instances[T, F]) extends Contravariant[F]:
final override def contramap[A, B](fa: F[A])(f: B => A): F[B] =
inst.map(fa: F[A])([f[_]] => (tf: T[f], fa: f[A]) => tf.contramap(fa)(f))
inst.map(fa)([f[_]] => (T: T[f], fa: f[A]) => T.contramap(fa)(f))
39 changes: 25 additions & 14 deletions core/src/main/scala-3/cats/derived/DerivedEmptyK.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cats.derived

import alleycats.{Empty, EmptyK, Pure}
import alleycats.{Empty, EmptyK}
import shapeless3.deriving.{Const, K1}

import scala.annotation.implicitNotFound
Expand All @@ -21,21 +21,32 @@ object DerivedEmptyK:
import DerivedEmptyK.given
summonInline[DerivedEmptyK[F]].instance

given [T](using T: Empty[T]): DerivedEmptyK[Const[T]] =
new EmptyK[Const[T]]:
def empty[A] = T.empty
given [T](using T: Empty[T]): DerivedEmptyK[Const[T]] = new EmptyK[Const[T]]:
def empty[A]: T = T.empty

given [F[_], G[_]](using F: Or[F]): DerivedEmptyK[[x] =>> F[G[x]]] =
new EmptyK[[x] =>> F[G[x]]]:
def empty[A] = F.unify.empty
given nested[F[_], G[_]](using F: => Or[F]): DerivedEmptyK[[x] =>> F[G[x]]] = new EmptyK[[x] =>> F[G[x]]]:
lazy val f = F.unify
def empty[A]: F[G[A]] = f.empty

given [F[_], G[_]](using NotGiven[Or[F]])(using F: DerivedPure.Or[F], G: Or[G]): DerivedEmptyK[[x] =>> F[G[x]]] =
new EmptyK[[x] =>> F[G[x]]]:
def empty[A] = F.unify.pure(G.unify.empty)
given nested[F[_], G[_]](using NotGiven[Or[F]])(using
F: DerivedPure.Or[F],
G: => Or[G]
): DerivedEmptyK[[x] =>> F[G[x]]] = new EmptyK[[x] =>> F[G[x]]]:
val f = F.unify
lazy val g = G.unify
def empty[A]: F[G[A]] = f.pure(g.empty)

given product[F[_]](using inst: K1.ProductInstances[Or, F]): DerivedEmptyK[F] =
new EmptyK[F]:
def empty[A]: F[A] = inst.unify.construct([f[_]] => (E: EmptyK[f]) => E.empty[A])
given product[F[_]](using inst: K1.ProductInstances[Or, F]): DerivedEmptyK[F] = new EmptyK[F]:
val f = inst.unify
def empty[A]: F[A] = f.construct([f[_]] => (F: EmptyK[f]) => F.empty[A])

inline given coproduct[F[_]](using gen: K1.CoproductGeneric[F]): DerivedEmptyK[F] =
gen.withOnly[Or, EmptyK[F]]([f[x] <: F[x]] => (ek: Or[f]) => ek.unify.asInstanceOf[EmptyK[F]])
gen.withOnly[Or, EmptyK[F]]([f[x] <: F[x]] => (F: Or[f]) => F.unify.asInstanceOf[EmptyK[F]])

@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedEmptyK_F[F[_]: Or, G[_]]: DerivedEmptyK[[x] =>> F[G[x]]] = summon

@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedEmptyK_F[F[_]: DerivedPure.Or, G[_]: Or](
ev: NotGiven[Or[F]]
): DerivedEmptyK[[x] =>> F[G[x]]] = nested(using ev)
23 changes: 13 additions & 10 deletions core/src/main/scala-3/cats/derived/DerivedFoldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ object DerivedFoldable:
def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B): B = b
def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb

given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFoldable[[x] =>> F[G[x]]] =
F.unify.compose(G.unify)
given nested[F[_], G[_]](using F: => Or[F], G: => Or[G]): DerivedFoldable[[x] =>> F[G[x]]] =
new Derived.Lazy(() => F.unify.compose(G.unify)) with Foldable[[x] =>> F[G[x]]]:
export delegate.*

given [F[_]](using inst: K1.ProductInstances[Or, F]): DerivedFoldable[F] =
given K1.ProductInstances[Foldable, F] = inst.unify
Expand All @@ -34,18 +35,20 @@ object DerivedFoldable:
given K1.CoproductInstances[Foldable, F] = inst.unify
new Coproduct[Foldable, F] {}

trait Product[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F]) extends Foldable[F]:
@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedFoldable_F[F[_]: Or, G[_]: Or]: DerivedFoldable[[x] =>> F[G[x]]] = summon

trait Product[T[f[_]] <: Foldable[f], F[_]](using inst: K1.ProductInstances[T, F]) extends Foldable[F]:
final override def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B =
inst.foldLeft[A, B](fa)(b)([f[_]] => (acc: B, tf: T[f], fa: f[A]) => Continue(tf.foldLeft(fa, acc)(f)))
inst.foldLeft(fa)(b)([f[_]] => (b: B, F: T[f], fa: f[A]) => Continue(F.foldLeft(fa, b)(f)))

final override def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
inst.foldRight[A, Eval[B]](fa)(lb)(
[f[_]] => (tf: T[f], fa: f[A], acc: Eval[B]) => Continue(Eval.defer(tf.foldRight(fa, acc)(f)))
)
inst.foldRight(fa)(lb):
[f[_]] => (F: T[f], fa: f[A], lb: Eval[B]) => Continue(Eval.defer(F.foldRight(fa, lb)(f)))

trait Coproduct[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F]) extends Foldable[F]:
trait Coproduct[T[f[_]] <: Foldable[f], F[_]](using inst: K1.CoproductInstances[T, F]) extends Foldable[F]:
final override def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B =
inst.fold[A, B](fa)([f[_]] => (tf: T[f], fa: f[A]) => tf.foldLeft(fa, b)(f))
inst.fold(fa)([f[_]] => (F: T[f], fa: f[A]) => F.foldLeft(fa, b)(f))

final override def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
inst.fold[A, Eval[B]](fa)([f[_]] => (tf: T[f], fa: f[A]) => Eval.defer(tf.foldRight(fa, lb)(f)))
inst.fold(fa)([f[_]] => (F: T[f], fa: f[A]) => Eval.defer(F.foldRight(fa, lb)(f)))
26 changes: 18 additions & 8 deletions core/src/main/scala-3/cats/derived/DerivedFunctor.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cats.derived

import cats.{Contravariant, Functor}
import cats.Functor
import shapeless3.deriving.{Const, K1}

import scala.annotation.implicitNotFound
Expand All @@ -23,20 +23,30 @@ object DerivedFunctor:
given [T]: DerivedFunctor[Const[T]] = new Functor[Const[T]]:
def map[A, B](fa: T)(f: A => B): T = fa

given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFunctor[[x] =>> F[G[x]]] =
F.unify.compose(G.unify)
given nested[F[_], G[_]](using F: => Or[F], G: => Or[G]): DerivedFunctor[[x] =>> F[G[x]]] =
new Derived.Lazy(() => F.unify.compose(G.unify)) with Functor[[x] =>> F[G[x]]]:
export delegate.*

given [F[_], G[_]](using
given nested[F[_], G[_]](using
F: DerivedContravariant.Or[F],
G: DerivedContravariant.Or[G]
): DerivedFunctor[[x] =>> F[G[x]]] =
given Contravariant[G] = G.unify
F.unify.compose[G]
F.unify.compose(G.unify)

given [F[_]](using inst: => K1.Instances[Or, F]): DerivedFunctor[F] =
given K1.Instances[Functor, F] = inst.unify
new Generic[Functor, F] {}

trait Generic[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F]) extends Functor[F]:
@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedFunctor_F[F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFunctor[[x] =>> F[G[x]]] =
nested(using F, G)

@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedFunctor_F[F[_], G[_]](using
F: DerivedContravariant.Or[F],
G: DerivedContravariant.Or[G]
): DerivedFunctor[[x] =>> F[G[x]]] = nested(using F, G)

trait Generic[T[f[_]] <: Functor[f], F[_]](using inst: K1.Instances[T, F]) extends Functor[F]:
final override def map[A, B](fa: F[A])(f: A => B): F[B] =
inst.map(fa: F[A])([f[_]] => (tf: T[f], fa: f[A]) => tf.map(fa)(f))
inst.map(fa)([f[_]] => (F: T[f], fa: f[A]) => F.map(fa)(f))
19 changes: 10 additions & 9 deletions core/src/main/scala-3/cats/derived/DerivedInvariant.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package cats.derived

import cats.{Contravariant, Functor, Invariant}
import cats.Invariant
import shapeless3.deriving.{Const, K1}

import scala.annotation.implicitNotFound
import scala.compiletime.*
import scala.util.NotGiven

@implicitNotFound("""Could not derive an instance of Invariant[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
Expand All @@ -23,15 +22,17 @@ object DerivedInvariant:
given [T]: DerivedInvariant[Const[T]] = new Invariant[Const[T]]:
def imap[A, B](fa: T)(f: A => B)(g: B => A): T = fa

given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedInvariant[[x] =>> F[G[x]]] =
given Invariant[G] = G.unify
F.unify.compose[G]
given nested[F[_], G[_]](using F: => Or[F], G: => Or[G]): DerivedInvariant[[x] =>> F[G[x]]] =
new Derived.Lazy(() => F.unify.compose(G.unify)) with Invariant[[x] =>> F[G[x]]]:
export delegate.*

given [F[_]](using inst: => K1.Instances[Or, F]): DerivedInvariant[F] =
given K1.Instances[Invariant, F] = inst.unify
new Generic[Invariant, F] {}

trait Generic[T[x[_]] <: Invariant[x], F[_]](using inst: K1.Instances[T, F]) extends Invariant[F]:
def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B] = inst.map(fa)(
[t[_]] => (inv: T[t], t0: t[A]) => inv.imap(t0)(f)(g)
)
@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedInvariant_F[F[_]: Or, G[_]: Or]: DerivedInvariant[[x] =>> F[G[x]]] = summon

trait Generic[T[f[_]] <: Invariant[f], F[_]](using inst: K1.Instances[T, F]) extends Invariant[F]:
final override def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B] =
inst.map(fa)([f[_]] => (F: T[f], fa: f[A]) => F.imap(fa)(f)(g))
46 changes: 25 additions & 21 deletions core/src/main/scala-3/cats/derived/DerivedMonoidK.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,35 @@ object DerivedMonoidK:
summonInline[DerivedMonoidK[F]].instance

given [T](using T: Monoid[T]): DerivedMonoidK[Const[T]] = new MonoidK[Const[T]]:
final override def empty[A]: Const[T][A] = T.empty

final override def combineK[A](x: Const[T][A], y: Const[T][A]) = T.combine(x, y)

given [F[_], G[_]](using F: Or[F]): DerivedMonoidK[[x] =>> F[G[x]]] =
F.unify.compose[G]

given [F[_], G[_]](using
N: NotGiven[Or[F]],
F0: DerivedApplicative.Or[F],
G0: Or[G]
): DerivedMonoidK[[x] =>> F[G[x]]] =
new MonoidK[[x] =>> F[G[x]]]:
val F: Applicative[F] = F0.unify
val G: MonoidK[G] = G0.unify

final override def empty[A]: F[G[A]] = F.pure(G.empty[A])

final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.map2(x, y)(G.combineK(_, _))
def empty[A]: T = T.empty
def combineK[A](x: T, y: T): T = T.combine(x, y)

given nested[F[_], G[_]](using F: => Or[F]): DerivedMonoidK[[x] =>> F[G[x]]] =
new Derived.Lazy(() => F.unify.compose[G]) with MonoidK[[x] =>> F[G[x]]]:
export delegate.*

given nested[F[_], G[_]](using NotGiven[Or[F]])(using
F: DerivedApplicative.Or[F],
G: => Or[G]
): DerivedMonoidK[[x] =>> F[G[x]]] = new MonoidK[[x] =>> F[G[x]]]:
val f: Applicative[F] = F.unify
lazy val g: MonoidK[G] = G.unify
def empty[A]: F[G[A]] = f.pure(g.empty[A])
def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = f.map2(x, y)(g.combineK)

given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedMonoidK[F] =
given K1.ProductInstances[MonoidK, F] = inst.unify
new Product[MonoidK, F] {}

trait Product[T[x[_]] <: MonoidK[x], F[_]](using inst: K1.ProductInstances[T, F])
@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedMonoidK_F[F[_]: Or, G[_]]: DerivedMonoidK[[x] =>> F[G[x]]] = summon

@deprecated("Kept for binary compatibility", "3.2.0")
private[derived] def given_DerivedMonoidK_F[F[_]: DerivedApplicative.Or, G[_]: Or](
ev: NotGiven[Or[F]]
): DerivedMonoidK[[x] =>> F[G[x]]] = nested(using ev)

trait Product[T[f[_]] <: MonoidK[f], F[_]](using inst: K1.ProductInstances[T, F])
extends MonoidK[F],
DerivedSemigroupK.Product[T, F]:
final override def empty[A]: F[A] = inst.construct([t[_]] => (emp: T[t]) => emp.empty[A])
final override def empty[A]: F[A] = inst.construct([f[_]] => (F: T[f]) => F.empty[A])
Loading