Skip to content
Closed
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
18 changes: 8 additions & 10 deletions core/src/main/scala-3/cats/derived/empty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cats.derived

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

object empty extends EmptyDerivation

Expand All @@ -11,20 +11,18 @@ trait DerivedEmpty[A] extends Empty[A]:
lazy val empty: A = emptyValue()

object DerivedEmpty:
inline given [A]: DerivedEmpty[A] = summonFrom {
case given Empty[A] => delegated
case given K0.ProductInstances[DerivedEmpty, A] => product
type Of[A] = Empty[A] OrElse DerivedEmpty[A]

inline given derived[A]: DerivedEmpty[A] = summonFrom {
case given K0.ProductInstances[Of, A] => product
case given K0.CoproductGeneric[A] => coproduct
}

def delegated[A](using A: => Empty[A]): DerivedEmpty[A] =
() => A.empty

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

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

trait EmptyDerivation:
extension (E: Empty.type)
Expand Down
45 changes: 21 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,62 +2,59 @@ 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:
object DerivedFoldable:
type Of[F[_]] = Alt1[Foldable, DerivedFoldable, F]

given const[T]: DerivedFoldable[Const[T]] with
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[_], G[_]](using F: Of[F], G: Of[G]): DerivedFoldable[[x] =>> F[G[x]]] with
private val underlying = F.unify `compose` G.unify
export underlying._

given composed[F[_]: DerivedFoldable, G[_]: DerivedFoldable]: DerivedFoldable[[x] =>> F[G[x]]] =
new Delegated(Foldable[F].compose[G])
inline given derived[F[_]]: DerivedFoldable[F] = summonFrom {
case given K1.ProductInstances[Of, F] => product
case given K1.CoproductInstances[Of, F] => coproduct
}

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

def coproduct[F[_]](using K1.CoproductInstances[DerivedFoldable, F]): DerivedFoldable[F] =
new Coproduct[DerivedFoldable, F] {}
def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedFoldable[F] =
new Coproduct[Of, F] {}

trait Product[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F])
trait Product[T[x[_]] <: Of[x], F[_]](using inst: K1.ProductInstances[T, F])
extends DerivedFoldable[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]) =>
Continue(tf.foldLeft(fa, acc)(f))
Continue(tf.unify.foldLeft(fa, acc)(f))
}

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)))
Continue(Eval.defer(tf.unify.foldRight(fa, acc)(f)))
}

trait Coproduct[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F])
trait Coproduct[T[x[_]] <: Of[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends DerivedFoldable[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]) =>
tf.foldLeft(fa, b)(f)
tf.unify.foldLeft(fa, b)(f)
}

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))
Eval.defer(tf.unify.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
33 changes: 15 additions & 18 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,37 @@ 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:
object DerivedFunctor:
type Of[F[_]] = Alt1[Functor, DerivedFunctor, F]

given const[T]: DerivedFunctor[Const[T]] with
def map[A, B](fa: T)(f: A => B): T = fa

given delegated[F[_]](using F: => Functor[F]): DerivedFunctor[F] =
new Delegated(F)
given composed[F[_], G[_]](using F: Of[F], G: Of[G]): DerivedFunctor[[x] =>> F[G[x]]] with
private val underlying = F.unify `compose` G.unify
export underlying._

given composed[F[_]: DerivedFunctor, G[_]: DerivedFunctor]: DerivedFunctor[[x] =>> F[G[x]]] =
new Delegated(Functor[F].compose[G])
inline given derived[F[_]]: DerivedFunctor[F] = summonFrom {
case given K1.ProductInstances[Of, F] => generic
case given K1.CoproductInstances[Of, F] => generic
}

def generic[F[_]](using K1.Instances[DerivedFunctor, F]): DerivedFunctor[F] =
new Generic[DerivedFunctor, F] {}
def generic[F[_]](using K1.Instances[Of, F]): DerivedFunctor[F] =
new Generic[Of, F] {}

trait Generic[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F])
trait Generic[T[x[_]] <: Of[x], F[_]](using inst: K1.Instances[T, F])
extends DerivedFunctor[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)
tf.unify.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
17 changes: 17 additions & 0 deletions core/src/main/scala-3/cats/derived/orelse.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cats.derived

import scala.compiletime._

final case class OrElse[+A, +B](unify: A | B)
object OrElse:
inline given [A, B]: (A OrElse B) = summonFrom {
case a: A => OrElse(a)
case b: B => OrElse(b)
}

final case class Alt1[+A[_[_]], +B[_[_]], F[_]](unify: A[F] | B[F])
object Alt1:
inline given [A[_[_]], B[_[_]], F[_]]: Alt1[A, B, F] = summonFrom {
case af: A[F] => Alt1(af)
case bf: B[F] => Alt1(bf)
}
58 changes: 27 additions & 31 deletions core/src/main/scala-3/cats/derived/reducible.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,33 @@ 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)
trait DerivedReducible[F[_]] extends DerivedFoldable[F], Reducible[F]
object DerivedReducible:
type Of[F[_]] = Alt1[Reducible, DerivedReducible, F]

given composed[F[_]: DerivedReducible, G[_]: DerivedReducible]: DerivedReducible[[x] =>> F[G[x]]] =
new Delegated(Reducible[F].compose[G])
given composed[F[_], G[_]](using F: Of[F], G: Of[G]): DerivedReducible[[x] =>> F[G[x]]] with
private val underlying = F.unify `compose` G.unify
export underlying._

inline given derived[F[_]]: DerivedReducible[F] = summonFrom {
case gen: K1.ProductGeneric[F] =>
given K1.ProductGeneric[F] = gen
product(K1.summonFirst[Of, gen.MirroredElemTypes, Const[Any]])
case given K1.CoproductInstances[Of, F] =>
coproduct
}

def product[F[_]](ev: Reducible[?])(using K1.ProductInstances[DerivedFoldable, F]): DerivedReducible[F] =
new Product[DerivedFoldable, F](ev) {}
def product[F[_]](ev: Of[Const[Any]])(using K1.ProductInstances[Of, F]): DerivedReducible[F] =
new Product[Of, F](ev) {}

def coproduct[F[_]](using K1.CoproductInstances[DerivedReducible, F]): DerivedReducible[F] =
new Coproduct[DerivedReducible, F] {}
def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedReducible[F] =
new Coproduct[Of, F] {}

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

private val evalNone = Eval.now(None)
Expand All @@ -29,44 +37,32 @@ object DerivedReducible extends DerivedReducibleLowPriority:
inst.foldLeft[A, Option[B]](fa)(None)(
[f[_]] => (acc: Option[B], tf: T[f], fa: f[A]) =>
acc match
case Some(b) => Continue(Some(tf.foldLeft(fa, b)(g)))
case None => Continue(tf.reduceLeftToOption(fa)(f)(g))
case Some(b) => Continue(Some(tf.unify.foldLeft(fa, b)(g)))
case None => Continue(tf.unify.reduceLeftToOption(fa)(f)(g))
).get

def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] =
inst.foldRight[A, Eval[Option[B]]](fa)(evalNone)(
[f[_]] => (tf: T[f], fa: f[A], acc: Eval[Option[B]]) =>
Continue(acc.flatMap {
case Some(b) => tf.foldRight(fa, Eval.now(b))(g).map(Some.apply)
case None => tf.reduceRightToOption(fa)(f)(g)
case Some(b) => tf.unify.foldRight(fa, Eval.now(b))(g).map(Some.apply)
case None => tf.unify.reduceRightToOption(fa)(f)(g)
})
).map(_.get)

trait Coproduct[T[x[_]] <: Reducible[x], F[_]](using inst: K1.CoproductInstances[T, F])
trait Coproduct[T[x[_]] <: Of[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends DerivedFoldable.Coproduct[T, F], DerivedReducible[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]) =>
tf.reduceLeftTo(fa)(f)(g)
tf.unify.reduceLeftTo(fa)(f)(g)
}

def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] =
inst.fold[A, Eval[B]](fa) { [f[_]] => (tf: T[f], fa: f[A]) =>
Eval.defer(tf.reduceRightTo(fa)(f)(g))
Eval.defer(tf.unify.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
43 changes: 20 additions & 23 deletions core/src/main/scala-3/cats/derived/traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +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:
trait DerivedTraverse[F[_]] extends DerivedFunctor[F], DerivedFoldable[F], Traverse[F]
object DerivedTraverse:
type Of[F[_]] = Alt1[Traverse, DerivedTraverse, F]

given const[T]: DerivedTraverse[Const[T]] with
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[_], G[_]](using F: Of[F], G: Of[G]): DerivedTraverse[[x] =>> F[G[x]]] with
private val underlying = F.unify `compose` G.unify
export underlying._

given composed[F[_]: DerivedTraverse, G[_]: DerivedTraverse]: DerivedTraverse[[x] =>> F[G[x]]] =
new Delegated(Traverse[F].compose[G])
inline given derived[F[_]]: DerivedTraverse[F] = summonFrom {
case given K1.ProductInstances[Of, F] => product
case given K1.CoproductInstances[Of, F] => coproduct
}

def product[F[_]](using K1.ProductInstances[DerivedTraverse, F]): DerivedTraverse[F] =
new Product[DerivedTraverse, F] {}
def product[F[_]](using K1.ProductInstances[Of, F]): DerivedTraverse[F] =
new Product[Of, F] {}

def coproduct[F[_]](using K1.CoproductInstances[DerivedTraverse, F]): DerivedTraverse[F] =
new Coproduct[DerivedTraverse, F] {}
def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedTraverse[F] =
new Coproduct[Of, F] {}

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

def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] =
Expand All @@ -37,10 +42,10 @@ object DerivedTraverse extends DerivedTraverseLowPriority:
} { [a, b] => (gf: G[a => b], ga: G[a]) =>
G.ap(gf)(ga)
} { [f[_]] => (tf: T[f], fa: f[A]) =>
tf.traverse(fa)(f)
tf.unify.traverse(fa)(f)
}

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

def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] =
Expand All @@ -51,17 +56,9 @@ object DerivedTraverse extends DerivedTraverseLowPriority:
} { [a, b] => (gf: G[a => b], ga: G[a]) =>
G.ap(gf)(ga)
} { [f[_]] => (tf: T[f], fa: f[A]) =>
tf.traverse(fa)(f)
tf.unify.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
5 changes: 4 additions & 1 deletion core/src/test/scala-3/cats/derived/EmptyTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ 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
case Node(left: IntTree, value: Int, right: IntTree)

@main def test() =
println(Empty[Foo].empty)
2 changes: 1 addition & 1 deletion core/src/test/scala-3/cats/derived/ReducibleTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import cats.Reducible
import cats.data.NonEmptyList
import cats.derived.all._

class ReducibleTests {
object ReducibleTests {

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

Expand Down