Skip to content
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
35 changes: 35 additions & 0 deletions core/src/main/scala-3/cats/derived/derived.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cats.derived

import shapeless3.deriving.*
import scala.annotation.*
import scala.compiletime.*

opaque type Derived[A] = A
object Derived:
def apply[A](instance: A): Derived[A] = instance
extension [A](derived: Derived[A]) def instance: A = derived
given [A]: Conversion[A, Derived[A]] = apply

type Or0[F[_]] = [x] =>> Or[F[x]]
type Or1[F[_[_]]] = [x[_]] =>> Or[F[x]]
type Or11[F[_[_[_]]]] = [x[_[_]]] =>> Or[F[x]]
type Or2[F[_[_, _]]] = [x[_, _]] =>> Or[F[x]]

opaque type Or[A] = A
object Or extends OrInstances:
def apply[A](instance: A): Or[A] = instance
extension [A](or: Or[A]) def unify: A = or
extension [I[f[_], t] <: K0.Instances[f, t], F[_], T]
(inst: I[Or0[F], T]) @targetName("unifyK0") def unify: I[F, T] = inst
extension [I[f[_[_]], t[_]] <: K1.Instances[f, t], F[_[_]], T[_]]
(inst: I[Or1[F], T]) @targetName("unifyK1") def unify: I[F, T] = inst
extension [I[f[_[_[_]]], t[_[_]]] <: K11.Instances[f, t], F[_[_[_]]], T[_[_]]]
(inst: I[Or11[F], T]) @targetName("unifyK11") def unify: I[F, T] = inst
extension [I[f[_[_, _]], t[_, _]] <: K2.Instances[f, t], F[_[_, _]], T[_, _]]
(inst: I[Or2[F], T]) @targetName("unifyK2") def unify: I[F, T] = inst

sealed abstract class OrInstances:
inline given [A]: Derived.Or[A] = summonFrom {
case instance: A => Derived.Or(instance)
case derived: Derived[A] => Derived.Or(derived.instance)
}
29 changes: 11 additions & 18 deletions core/src/main/scala-3/cats/derived/empty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,23 @@ package cats.derived

import alleycats.Empty
import shapeless3.deriving.K0
import scala.compiletime.summonFrom
import scala.compiletime.*

object empty extends EmptyDerivation

trait DerivedEmpty[A] extends Empty[A]:
protected def emptyValue(): A
lazy val empty: A = emptyValue()

type DerivedEmpty[A] = Derived[Empty[A]]
object DerivedEmpty:
inline given [A]: DerivedEmpty[A] = summonFrom {
case given Empty[A] => delegated
case given K0.ProductInstances[DerivedEmpty, A] => product
case given K0.CoproductGeneric[A] => coproduct
}

def delegated[A](using A: => Empty[A]): DerivedEmpty[A] =
() => A.empty
type Or[A] = Derived.Or[Empty[A]]
inline def apply[A]: Empty[A] =
import DerivedEmpty.given
summonInline[DerivedEmpty[A]].instance

def product[A](using inst: K0.ProductInstances[DerivedEmpty, A]): DerivedEmpty[A] =
() => inst.construct([A] => (A: DerivedEmpty[A]) => A.empty)
given product[A](using inst: K0.ProductInstances[Or, A]): DerivedEmpty[A] =
Empty(inst.unify.construct([A] => (A: Empty[A]) => A.empty))

inline def coproduct[A](using gen: K0.CoproductGeneric[A]): DerivedEmpty[A] =
K0.summonFirst[DerivedEmpty, gen.MirroredElemTypes, A]
inline given coproduct[A](using gen: K0.CoproductGeneric[A]): DerivedEmpty[A] =
K0.summonFirst[Or, gen.MirroredElemTypes, A].unify

trait EmptyDerivation:
extension (E: Empty.type)
def derived[A](using instance: DerivedEmpty[A]): Empty[A] = instance
inline def derived[A]: Empty[A] = DerivedEmpty[A]
42 changes: 18 additions & 24 deletions core/src/main/scala-3/cats/derived/foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,32 @@ package cats.derived

import cats.{Eval, Foldable}
import shapeless3.deriving.{Const, Continue, K1}
import scala.annotation.threadUnsafe
import scala.compiletime.*

object foldable extends FoldableDerivation, Instances

trait DerivedFoldable[F[_]] extends Foldable[F]
object DerivedFoldable extends DerivedFoldableLowPriority:
given const[T]: DerivedFoldable[Const[T]] with
type DerivedFoldable[F[_]] = Derived[Foldable[F]]
object DerivedFoldable:
type Or[F[_]] = Derived.Or[Foldable[F]]
inline def apply[F[_]]: Foldable[F] =
import DerivedFoldable.given
summonInline[DerivedFoldable[F]].instance

given [T]: DerivedFoldable[Const[T]] = new Foldable[Const[T]]:
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 delegated[F[_]](using F: => Foldable[F]): DerivedFoldable[F] =
new Delegated(F)

given composed[F[_]: DerivedFoldable, G[_]: DerivedFoldable]: DerivedFoldable[[x] =>> F[G[x]]] =
new Delegated(Foldable[F].compose[G])
given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFoldable[[x] =>> F[G[x]]] =
F.unify `compose` G.unify

def product[F[_]](using K1.ProductInstances[DerivedFoldable, F]): DerivedFoldable[F] =
new Product[DerivedFoldable, F] {}
given [F[_]](using inst: K1.ProductInstances[Or, F]): DerivedFoldable[F] =
new Product(using inst.unify) {}

def coproduct[F[_]](using K1.CoproductInstances[DerivedFoldable, F]): DerivedFoldable[F] =
new Coproduct[DerivedFoldable, F] {}
given [F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedFoldable[F] =
new Coproduct(using inst.unify) {}

trait Product[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F])
extends DerivedFoldable[F]:
extends Foldable[F]:

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]) =>
Expand All @@ -38,7 +40,7 @@ object DerivedFoldable extends DerivedFoldableLowPriority:
}

trait Coproduct[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends DerivedFoldable[F]:
extends Foldable[F]:

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]) =>
Expand All @@ -50,14 +52,6 @@ object DerivedFoldable extends DerivedFoldableLowPriority:
Eval.defer(tf.foldRight(fa, lb)(f))
}

private final class Delegated[F[_]](F: => Foldable[F]) extends DerivedFoldable[F]:
@threadUnsafe private lazy val underlying = F
export underlying._

private[derived] sealed abstract class DerivedFoldableLowPriority:
inline given derived[F[_]](using gen: K1.Generic[F]): DerivedFoldable[F] =
gen.derive(DerivedFoldable.product, DerivedFoldable.coproduct)

trait FoldableDerivation:
extension (F: Foldable.type)
def derived[F[_]](using instance: DerivedFoldable[F]): Foldable[F] = instance
inline def derived[F[_]]: Foldable[F] = DerivedFoldable[F]
36 changes: 15 additions & 21 deletions core/src/main/scala-3/cats/derived/functor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,34 @@ package cats.derived

import cats.Functor
import shapeless3.deriving.{Const, K1}
import scala.annotation.threadUnsafe
import scala.compiletime.*

object functor extends FunctorDerivation, Instances

trait DerivedFunctor[F[_]] extends Functor[F]
object DerivedFunctor extends DerivedFunctorLowPriority:
given const[T]: DerivedFunctor[Const[T]] with
def map[A, B](fa: T)(f: A => B): T = fa
type DerivedFunctor[F[_]] = Derived[Functor[F]]
object DerivedFunctor:
type Or[F[_]] = Derived.Or[Functor[F]]
inline def apply[F[_]]: Functor[F] =
import DerivedFunctor.given
summonInline[DerivedFunctor[F]].instance

given delegated[F[_]](using F: => Functor[F]): DerivedFunctor[F] =
new Delegated(F)
given [T]: DerivedFunctor[Const[T]] = new Functor[Const[T]]:
def map[A, B](fa: T)(f: A => B): T = fa

given composed[F[_]: DerivedFunctor, G[_]: DerivedFunctor]: DerivedFunctor[[x] =>> F[G[x]]] =
new Delegated(Functor[F].compose[G])
given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFunctor[[x] =>> F[G[x]]] =
F.unify `compose` G.unify

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

trait Generic[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F])
extends DerivedFunctor[F]:
extends Functor[F]:

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)
}

private final class Delegated[F[_]](F: => Functor[F]) extends DerivedFunctor[F]:
@threadUnsafe private lazy val underlying = F
export underlying._

private[derived] sealed abstract class DerivedFunctorLowPriority:
inline given derived[F[_]](using K1.Generic[F]): DerivedFunctor[F] =
DerivedFunctor.generic

trait FunctorDerivation:
extension (F: Functor.type)
def derived[F[_]](using instance: DerivedFunctor[F]): Functor[F] = instance
inline def derived[F[_]]: Functor[F] = DerivedFunctor[F]
48 changes: 22 additions & 26 deletions core/src/main/scala-3/cats/derived/reducible.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,34 @@ package cats.derived

import cats.{Eval, Foldable, Reducible}
import shapeless3.deriving.{Continue, Const, K1}
import scala.annotation.threadUnsafe
import scala.compiletime.*

object reducible extends ReducibleDerivation

trait DerivedReducible[F[_]] extends Reducible[F]
object DerivedReducible extends DerivedReducibleLowPriority:
given delegated[F[_]](using F: => Reducible[F]): DerivedReducible[F] =
new Delegated(F)
type DerivedReducible[F[_]] = Derived[Reducible[F]]
object DerivedReducible:
type Or[F[_]] = Derived.Or[Reducible[F]]
inline def apply[F[_]]: Reducible[F] =
import DerivedFoldable.given
import DerivedReducible.given
summonInline[DerivedReducible[F]].instance

given composed[F[_]: DerivedReducible, G[_]: DerivedReducible]: DerivedReducible[[x] =>> F[G[x]]] =
new Delegated(Reducible[F].compose[G])
given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedReducible[[x] =>> F[G[x]]] =
F.unify `compose` G.unify

def product[F[_]](ev: Reducible[?])(using K1.ProductInstances[DerivedFoldable, F]): DerivedReducible[F] =
new Product[DerivedFoldable, F](ev) {}
def product[F[_]](ev: Reducible[?])(using inst: K1.ProductInstances[DerivedFoldable.Or, F]): DerivedReducible[F] =
given K1.ProductInstances[Foldable, F] = inst.unify
new Product[Foldable, F](ev) {}

def coproduct[F[_]](using K1.CoproductInstances[DerivedReducible, F]): DerivedReducible[F] =
new Coproduct[DerivedReducible, F] {}
inline given [F[_]](using gen: K1.ProductGeneric[F]): DerivedReducible[F] =
product(K1.summonFirst[Or, gen.MirroredElemTypes, Const[Any]].unify)

given [F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedReducible[F] =
given K1.CoproductInstances[Reducible, F] = inst.unify
new Coproduct[Reducible, F] {}

trait Product[T[x[_]] <: Foldable[x], F[_]](ev: Reducible[?])(using inst: K1.ProductInstances[T, F])
extends DerivedFoldable.Product[T, F], DerivedReducible[F]:
extends DerivedFoldable.Product[T, F], Reducible[F]:

private val evalNone = Eval.now(None)

Expand All @@ -43,7 +51,7 @@ object DerivedReducible extends DerivedReducibleLowPriority:
).map(_.get)

trait Coproduct[T[x[_]] <: Reducible[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends DerivedFoldable.Coproduct[T, F], DerivedReducible[F]:
extends DerivedFoldable.Coproduct[T, F], Reducible[F]:

def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B =
inst.fold[A, B](fa) { [f[_]] => (tf: T[f], fa: f[A]) =>
Expand All @@ -55,18 +63,6 @@ object DerivedReducible extends DerivedReducibleLowPriority:
Eval.defer(tf.reduceRightTo(fa)(f)(g))
}

private final class Delegated[F[_]](F: => Reducible[F]) extends DerivedReducible[F]:
@threadUnsafe private lazy val underlying = F
export underlying._

private[derived] sealed abstract class DerivedReducibleLowPriority:
inline given derived[F[_]](using gen: K1.Generic[F]): DerivedReducible[F] =
inline gen match
case given K1.ProductGeneric[F] =>
DerivedReducible.product(K1.summonFirst[DerivedReducible, gen.MirroredElemTypes, Const[Any]])
case given K1.CoproductGeneric[F] =>
DerivedReducible.coproduct

trait ReducibleDerivation:
extension (F: Reducible.type)
def derived[F[_]](using instance: DerivedReducible[F]): Reducible[F] = instance
inline def derived[F[_]]: Reducible[F] = DerivedReducible[F]
44 changes: 20 additions & 24 deletions core/src/main/scala-3/cats/derived/traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,36 @@ package cats.derived

import cats.{Applicative, Eval, Traverse}
import shapeless3.deriving.{Const, Continue, K1}
import scala.annotation.threadUnsafe
import scala.compiletime.*

object traverse extends TraverseDerivation, Instances

trait DerivedTraverse[F[_]] extends Traverse[F]
object DerivedTraverse extends DerivedTraverseLowPriority:
given const[T]: DerivedTraverse[Const[T]] with
type DerivedTraverse[F[_]] = Derived[Traverse[F]]
object DerivedTraverse:
type Or[F[_]] = Derived.Or[Traverse[F]]
inline def apply[F[_]]: Traverse[F] =
import DerivedTraverse.given
summonInline[DerivedTraverse[F]].instance

given [T]: DerivedTraverse[Const[T]] = new Traverse[Const[T]]:
override def map[A, B](fa: T)(f: A => B): T = fa
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
def traverse[G[_], A, B](fa: T)(f: A => G[B])(using G: Applicative[G]): G[T] = G.pure(fa)

given delegated[F[_]](using F: => Traverse[F]): DerivedTraverse[F] =
new Delegated(F)

given composed[F[_]: DerivedTraverse, G[_]: DerivedTraverse]: DerivedTraverse[[x] =>> F[G[x]]] =
new Delegated(Traverse[F].compose[G])
given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedTraverse[[x] =>> F[G[x]]] =
F.unify `compose` G.unify

def product[F[_]](using K1.ProductInstances[DerivedTraverse, F]): DerivedTraverse[F] =
new Product[DerivedTraverse, F] {}
given [F[_]](using inst: K1.ProductInstances[Or, F]): DerivedTraverse[F] =
given K1.ProductInstances[Traverse, F] = inst.unify
new Product[Traverse, F] {}

def coproduct[F[_]](using K1.CoproductInstances[DerivedTraverse, F]): DerivedTraverse[F] =
new Coproduct[DerivedTraverse, F] {}
given [F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedTraverse[F] =
given K1.CoproductInstances[Traverse, F] = inst.unify
new Coproduct[Traverse, F] {}

trait Product[T[x[_]] <: Traverse[x], F[_]](using inst: K1.ProductInstances[T, F])
extends DerivedFunctor.Generic[T, F], DerivedFoldable.Product[T, F], DerivedTraverse[F]:
extends DerivedFunctor.Generic[T, F], DerivedFoldable.Product[T, F], Traverse[F]:

def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] =
inst.traverse[A, G, B](fa) { [a, b] => (ga: G[a], f: a => b) =>
Expand All @@ -41,7 +45,7 @@ object DerivedTraverse extends DerivedTraverseLowPriority:
}

trait Coproduct[T[x[_]] <: Traverse[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends DerivedFunctor.Generic[T, F], DerivedFoldable.Coproduct[T, F], DerivedTraverse[F]:
extends DerivedFunctor.Generic[T, F], DerivedFoldable.Coproduct[T, F], Traverse[F]:

def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] =
inst.traverse[A, G, B](fa) { [a, b] => (ga: G[a], f: a => b) =>
Expand All @@ -54,14 +58,6 @@ object DerivedTraverse extends DerivedTraverseLowPriority:
tf.traverse(fa)(f)
}

private final class Delegated[F[_]](F: => Traverse[F]) extends DerivedTraverse[F]:
@threadUnsafe private lazy val underlying = F
export underlying._

private[derived] sealed abstract class DerivedTraverseLowPriority:
inline given derived[F[_]](using gen: K1.Generic[F]): DerivedTraverse[F] =
gen.derive(DerivedTraverse.product, DerivedTraverse.coproduct)

trait TraverseDerivation:
extension (F: Traverse.type)
def derived[F[_]](using instance: DerivedTraverse[F]): Traverse[F] = instance
inline def derived[F[_]]: Traverse[F] = DerivedTraverse[F]
2 changes: 1 addition & 1 deletion core/src/test/scala-3/cats/derived/EmptyTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import alleycats._
import cats._
import cats.derived.all._

class EmptyTests:
object EmptyTests:
case class Foo(i: Int, b: IntTree) derives Empty
enum IntTree:
case Leaf
Expand Down
15 changes: 9 additions & 6 deletions core/src/test/scala-3/cats/derived/ReducibleTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ package cats.derived

import cats.Reducible
import cats.data.NonEmptyList
import cats.derived.all._

class ReducibleTests {
import cats.derived.all.*

object ReducibleTests:
case class Box[A](value: A) derives Reducible

sealed trait OneOrMany[+A] derives Reducible
Expand All @@ -26,6 +25,10 @@ class ReducibleTests {
case Non
case Con(v: A, r: MyList[A])

summon[DerivedFunctor[MyList]]
summon[DerivedReducible[NonEmptyTree]]
}
import cats._

DerivedFunctor[MyList]
DerivedReducible[NonEmptyTree]

@main def run() =
println(DerivedFunctor[ReducibleTests.MyList].map(ReducibleTests.MyList.Con(42, ReducibleTests.MyList.Non))(_.toString))