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
1 change: 1 addition & 0 deletions core/src/main/scala-3/cats/derived/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ object all extends
MonoidKDerivation,
OrderDerivation,
PartialOrderDerivation,
ReducibleDerivation,
SemigroupDerivation,
SemigroupKDerivation,
ShowDerivation,
Expand Down
76 changes: 48 additions & 28 deletions core/src/main/scala-3/cats/derived/foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,62 @@ package cats.derived

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

object foldable extends FoldableDerivation, Instances

trait ProductFoldable[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F])
extends Foldable[F]:
trait DerivedFoldable[F[_]] extends Foldable[F]
object DerivedFoldable extends DerivedFoldableLowPriority:
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

def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B =
inst.foldLeft[A, B](fa)(b)(
[t[_]] => (acc: B, fd: T[t], t0: t[A]) => Continue(fd.foldLeft(t0, acc)(f))
)
given delegated[F[_]](using F: => Foldable[F]): DerivedFoldable[F] =
new Delegated(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)(
[t[_]] => (fd: T[t], t0: t[A], acc: Eval[B]) => Continue(fd.foldRight(t0, acc)(f))
)
given composed[F[_]: DerivedFoldable, G[_]: DerivedFoldable]: DerivedFoldable[[x] =>> F[G[x]]] =
new Delegated(Foldable[F].compose[G])

trait CoproductFoldable[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends Foldable[F]:
def product[F[_]](using K1.ProductInstances[DerivedFoldable, F]): DerivedFoldable[F] =
new Product[DerivedFoldable, F] {}

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

def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B =
inst.fold[A, B](fa)(
[t[_]] => (fd: T[t], t0: t[A]) => fd.foldLeft(t0, b)(f)
)
trait Product[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F])
extends DerivedFoldable[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)(
[t[_]] => (fd: T[t], t0: t[A]) => fd.foldRight(t0, lb)(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))
}

trait FoldableDerivation:
extension (F: Foldable.type)
inline def derived[F[_]](using gen: K1.Generic[F]): Foldable[F] =
gen.derive(productFoldable[F], coproductFoldable[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)))
}

trait Coproduct[T[x[_]] <: Foldable[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)
}

given productFoldable[F[_]](using inst: => K1.ProductInstances[Foldable, F]): Foldable[F] =
new ProductFoldable[Foldable, 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))
}

given coproductFoldable[F[_]](using inst: => K1.CoproductInstances[Foldable, F]): Foldable[F] =
new CoproductFoldable[Foldable, 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
38 changes: 30 additions & 8 deletions core/src/main/scala-3/cats/derived/functor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,40 @@ package cats.derived

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

object functor extends FunctorDerivation, Instances

trait GenericFunctor[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F])
extends Functor[F]:
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

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

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

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

trait Generic[T[x[_]] <: Functor[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)
}

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)
inline def derived[F[_]](using gen: K1.Generic[F]): Functor[F] =
new GenericFunctor[Functor, F]{}
def derived[F[_]](using instance: DerivedFunctor[F]): Functor[F] = instance
72 changes: 72 additions & 0 deletions core/src/main/scala-3/cats/derived/reducible.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cats.derived

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

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)

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

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

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

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

private val evalNone = Eval.now(None)

def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B =
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))
).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)
})
).map(_.get)

trait Coproduct[T[x[_]] <: Reducible[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)
}

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

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
74 changes: 54 additions & 20 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,66 @@ package cats.derived

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

object traverse extends TraverseDerivation, Instances

trait ProductTraverse[T[x[_]] <: Traverse[x], F[_]](using inst: K1.ProductInstances[T, F])
extends GenericFunctor[T, F], ProductFoldable[T, F], Traverse[F]:
trait DerivedTraverse[F[_]] extends Traverse[F]
object DerivedTraverse extends DerivedTraverseLowPriority:
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)

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) => G.map(ga)(f))([a] => (x: a) => G.pure(x))([a,b] => (gf: G[a => b], ga: G[a]) => G.ap(gf)(ga))(
[t[_]] => (trav: T[t], t0: t[A]) => trav.traverse[G, A, B](t0)(f)
)
given delegated[F[_]](using F: => Traverse[F]): DerivedTraverse[F] =
new Delegated(F)

trait CoproductTraverse[T[x[_]] <: Traverse[x], F[_]](using inst: K1.CoproductInstances[T, F])
extends GenericFunctor[T, F], CoproductFoldable[T, F], Traverse[F]:
given composed[F[_]: DerivedTraverse, G[_]: DerivedTraverse]: DerivedTraverse[[x] =>> F[G[x]]] =
new Delegated(Traverse[F].compose[G])

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) => G.map(ga)(f))([a] => (x: a) => G.pure(x))([a,b] => (gf: G[a => b], ga: G[a]) => G.ap(gf)(ga))(
[t[_]] => (trav: T[t], t0: t[A]) => trav.traverse[G, A, B](t0)(f)
)
def product[F[_]](using K1.ProductInstances[DerivedTraverse, F]): DerivedTraverse[F] =
new Product[DerivedTraverse, F] {}

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

trait TraverseDerivation:
extension (F: Traverse.type)
inline def derived[F[_]](using gen: K1.Generic[F]): Traverse[F] =
gen.derive(productTraverse, coproductTraverse)
trait Product[T[x[_]] <: Traverse[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]] =
inst.traverse[A, G, B](fa) { [a, b] => (ga: G[a], f: a => b) =>
G.map(ga)(f)
} { [a] => (x: a) =>
G.pure(x)
} { [a, b] => (gf: G[a => b], ga: G[a]) =>
G.ap(gf)(ga)
} { [f[_]] => (tf: T[f], fa: f[A]) =>
tf.traverse(fa)(f)
}

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

given productTraverse[F[_]](using inst: => K1.ProductInstances[Traverse, F]): Traverse[F] =
new ProductTraverse[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) =>
G.map(ga)(f)
} { [a] => (x: a) =>
G.pure(x)
} { [a, b] => (gf: G[a => b], ga: G[a]) =>
G.ap(gf)(ga)
} { [f[_]] => (tf: T[f], fa: f[A]) =>
tf.traverse(fa)(f)
}

given coproductTraverse[F[_]](using inst: => K1.CoproductInstances[Traverse, F]): Traverse[F] =
new CoproductTraverse[Traverse, 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
3 changes: 1 addition & 2 deletions core/src/test/scala-3/cats/derived/FoldableTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cats.derived

import cats.Foldable
import cats.derived.all._
import cats.derived.all.given

class FoldableTests {

Expand All @@ -14,5 +13,5 @@ class FoldableTests {

sealed trait CList[A] derives Foldable
case object CNil extends CList[Nothing]
case class CCons[A](head: A, tail: CCons[A]) extends CList[A]
case class CCons[A](head: A, tail: CList[A]) extends CList[A]
}
3 changes: 1 addition & 2 deletions core/src/test/scala-3/cats/derived/FunctorTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cats.derived

import cats.Functor
import cats.derived.all._
import cats.derived.all.given

class FunctorTests {

Expand All @@ -14,5 +13,5 @@ class FunctorTests {

sealed trait CList[A] derives Functor
case object CNil extends CList[Nothing]
case class CCons[A](head: A, tail: CCons[A]) extends CList[A]
case class CCons[A](head: A, tail: CList[A]) extends CList[A]
}
31 changes: 31 additions & 0 deletions core/src/test/scala-3/cats/derived/ReducibleTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cats.derived

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

class ReducibleTests {

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

sealed trait OneOrMany[+A] derives Reducible
case class One[+A](value: A) extends OneOrMany[A]
case class Many[+A](values: NonEmptyList[A]) extends OneOrMany[A]

sealed trait CList[A] derives Reducible
case class COne[A](value: A) extends CList[A]
case class CCons[A](head: A, tail: CList[A]) extends CList[A]

case class NonEmptyTree[A](size: Int, value: A, tree: Tree[A])
enum Tree[+A]:
case Leaf
case Node(left: Tree[A], value: A, right: Tree[A])

case class Foo[A](value: A, xs: List[A])
enum MyList[+A]:
case Non
case Con(v: A, r: MyList[A])

summon[DerivedFunctor[MyList]]
summon[DerivedReducible[NonEmptyTree]]
}
3 changes: 1 addition & 2 deletions core/src/test/scala-3/cats/derived/TraverseTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cats.derived

import cats.Traverse
import cats.derived.all._
import cats.derived.all.given

class TraverseTests {

Expand All @@ -14,5 +13,5 @@ class TraverseTests {

sealed trait CList[A] derives Traverse
case object CNil extends CList[Nothing]
case class CCons[A](head: A, tail: CCons[A]) extends CList[A]
case class CCons[A](head: A, tail: CList[A]) extends CList[A]
}