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 K21 test example with BifunctorK type class #224

Merged
merged 3 commits into from
Aug 21, 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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package shapeless3.deriving

import cats.data.{EitherK, Tuple2K}

// ADTs

object adts:
Expand Down Expand Up @@ -49,6 +51,12 @@ object adts:
quantity: F[Int]
) derives FunctorK

enum Lattice[F[_], G[_]] derives BifunctorK:
case Top(value: F[Int])
case Bot(value: G[Int])
case Meet(value: Tuple2K[F, G, Int])
case Join(value: EitherK[F, G, Int])

sealed trait OptionD[T]:
def fold: T = this match
case Given(t) => t
Expand Down
13 changes: 13 additions & 0 deletions modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package shapeless3.deriving

import cats.Eval
import cats.data.{EitherK, Tuple2K}
import org.junit.Assert.*
import org.junit.Test
import shapeless3.deriving.adts.*
Expand Down Expand Up @@ -255,6 +256,18 @@ class DerivationTests:
val exp1 = HkCons(Some(42), HkCons(None, HkOne(Some(313))))
assert(FunctorK[HkNel].mapK(arg1)(OptionD.toOption) == exp1)

@Test
def bifunctorK(): Unit =
val bf = BifunctorK[Lattice]
val top = Lattice.Top[OptionD, OptionD](Given(100))
val bot = Lattice.Bot[OptionD, OptionD](Default(0))
val meet = Lattice.Meet[OptionD, OptionD](Tuple2K(Given(100), Default(0)))
val join = Lattice.Join[OptionD, OptionD](EitherK.left(Given(100)))
assert(bf.bimapK(top)(OptionD.fold, OptionD.toOption) == Lattice.Top[Id, Option](100))
assert(bf.bimapK(bot)(OptionD.fold, OptionD.toOption) == Lattice.Bot[Id, Option](None))
assert(bf.bimapK(meet)(OptionD.fold, OptionD.toOption) == Lattice.Meet[Id, Option](Tuple2K(100, None)))
assert(bf.bimapK(join)(OptionD.fold, OptionD.toOption) == Lattice.Join[Id, Option](EitherK.left(100)))

@Test
def bifunctor(): Unit =
val v0 = Bifunctor[ConsF]
Expand Down
82 changes: 82 additions & 0 deletions modules/deriving/src/test/scala/shapeless3/deriving/kinds.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package shapeless3.deriving

import shapeless3.deriving.internals.Kinds

import scala.Tuple.Union

object K21
extends Kind[
[_[_], _[_]] =>> Any,
[_[_], _[_]] =>> Tuple,
[t[_[_], _[_]]] =>> t[[_] =>> Any, [_] =>> Any],
[t[_[_], _[_]]] =>> [a[_], b[_]] =>> Kinds.Head[t[a, b]],
[t[_[_], _[_]]] =>> [a[_], b[_]] =>> Kinds.Tail[t[a, b]]
]:

type Id1[t] = [f[_], g[_]] =>> f[t]
type Id2[t] = [f[_], g[_]] =>> g[t]
type Const[c] = [t[_], u[_]] =>> c

extension [T[_[_], _[_]], A[_], B[_]](gen: ProductGeneric[T])
inline def toRepr(o: T[A, B]): gen.MirroredElemTypes[A, B] =
Tuple.fromProduct(o.asInstanceOf).asInstanceOf[gen.MirroredElemTypes[A, B]]
inline def fromRepr(r: gen.MirroredElemTypes[A, B]): T[A, B] =
gen.fromProduct(r.asInstanceOf).asInstanceOf[T[A, B]]

extension [T[_[_], _[_]], A[_], B[_]](gen: CoproductGeneric[T])
inline def toRepr(o: T[A, B]): Union[gen.MirroredElemTypes[A, B]] = o.asInstanceOf
inline def fromRepr(r: Union[gen.MirroredElemTypes[A, B]]): T[A, B] = r.asInstanceOf

extension [F[_[_[_], _[_]]], T[_[_], _[_]]](inst: Instances[F, T])
inline def map[A[_], B[_], R[_], S[_]](x: T[A, B])(f: [t[_[_], _[_]]] => (F[t], t[A, B]) => t[R, S]): T[R, S] =
inst.erasedMap(x)(f.asInstanceOf).asInstanceOf
inline def traverse[A[_], B[_], G[_], R[_], S[_]](x: T[A, B])(map: MapF[G])(pure: Pure[G])(ap: Ap[G])(
f: [t[_[_], _[_]]] => (F[t], t[A, B]) => G[t[R, S]]
): G[T[R, S]] =
inst.erasedTraverse(x)(map)(pure)(ap)(f.asInstanceOf).asInstanceOf

extension [F[_[_[_], _[_]]], T[_[_], _[_]]](inst: ProductInstances[F, T])
inline def construct[R[_], S[_]](f: [t[_[_], _[_]]] => F[t] => t[R, S]): T[R, S] =
inst.erasedConstruct(f.asInstanceOf).asInstanceOf
inline def constructA[G[_], R[_], S[_]](
f: [t[_[_], _[_]]] => F[t] => G[t[R, S]]
)(pure: Pure[G], map: MapF[G], ap: Ap[G]): G[T[R, S]] =
inst.erasedConstructA(f.asInstanceOf)(pure, map, ap).asInstanceOf
inline def constructM[G[_], R[_], S[_]](
f: [t[_[_], _[_]]] => F[t] => G[t[R, S]]
)(pure: Pure[G], map: MapF[G], tailRecM: TailRecM[G]): G[T[R, S]] =
inst.erasedConstructM(f.asInstanceOf)(pure, map, tailRecM).asInstanceOf
inline def map2[A[_], B[_], C[_], D[_], R[_], S[_]](x: T[A, B], y: T[C, D])(
f: [t[_[_], _[_]]] => (F[t], t[A, B], t[B, C]) => t[R, S]
): T[R, S] =
inst.erasedMap2(x, y)(f.asInstanceOf).asInstanceOf
inline def foldLeft[A[_], B[_], Acc](x: T[A, B])(i: Acc)(
f: [t[_[_], _[_]]] => (Acc, F[t], t[A, B]) => CompleteOr[Acc]
): Acc =
inst.erasedFoldLeft(x)(i)(f.asInstanceOf).asInstanceOf
inline def foldLeft2[A[_], B[_], C[_], D[_], Acc](x: T[A, B], y: T[C, D])(i: Acc)(
f: [t[_[_], _[_]]] => (Acc, F[t], t[A, B], t[C, D]) => CompleteOr[Acc]
): Acc =
inst.erasedFoldLeft2(x, y)(i)(f.asInstanceOf).asInstanceOf
inline def foldRight[A[_], B[_], Acc](x: T[A, B])(i: Acc)(
f: [t[_[_], _[_]]] => (F[t], t[A, B], Acc) => CompleteOr[Acc]
): Acc =
inst.erasedFoldRight(x)(i)(f.asInstanceOf).asInstanceOf
inline def foldRight2[A[_], B[_], C[_], D[_], Acc](x: T[A, B], y: T[C, D])(i: Acc)(
f: [t[_[_], _[_]]] => (F[t], t[A, B], t[C, D], Acc) => CompleteOr[Acc]
): Acc =
inst.erasedFoldRight2(x, y)(i)(f.asInstanceOf).asInstanceOf
inline def project[A[_], B[_], R](t: T[A, B])(p: Int)(f: [t[_[_], _[_]]] => (F[t], t[A, B]) => R): R =
inst.erasedProject(t)(p)(f.asInstanceOf).asInstanceOf

extension [F[_[_[_], _[_]]], T[_[_], _[_]]](inst: CoproductInstances[F, T])
inline def fold[A[_], B[_], R](x: T[A, B])(f: [t[a[_], b[_]] <: T[a, b]] => (F[t], t[A, B]) => R): R =
inst.erasedFold(x)(f.asInstanceOf).asInstanceOf
inline def fold2[A[_], B[_], C[_], D[_], R](x: T[A, B], y: T[C, D])(a: => R)(
f: [t[a[_], b[_]] <: T[a, b]] => (F[t], t[A, B], t[C, D]) => R
): R =
inst.erasedFold2(x, y)(a.asInstanceOf)(f.asInstanceOf).asInstanceOf
inline def fold2[A[_], B[_], C[_], D[_], R](x: T[A, B], y: T[C, D])(g: (Int, Int) => R)(
f: [t[a[_], b[_]] <: T[a, b]] => (F[t], t[A, B], t[C, D]) => R
): R =
inst.erasedFold2f(x, y)(g.asInstanceOf)(f.asInstanceOf).asInstanceOf
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ package shapeless3.deriving

import scala.annotation.tailrec
import scala.compiletime.*

import cats.Eval
import cats.data.{EitherK, Tuple2K}

// Type classes

Expand Down Expand Up @@ -342,6 +342,38 @@ object FunctorK:

inline def derived[F[_[_]]](using gen: K11.Generic[F]): FunctorK[F] = functorKGen

trait BifunctorK[F[_[_], _[_]]]:
def bimapK[A[_], B[_], C[_], D[_]](fab: F[A, B])(f: A ~> C, g: B ~> D): F[C, D]

object BifunctorK:
inline def apply[F[_[_], _[_]]](using bf: BifunctorK[F]): BifunctorK[F] = bf

given [T]: BifunctorK[K21.Id1[T]] with
def bimapK[A[_], B[_], C[_], D[_]](at: A[T])(f: A ~> C, g: B ~> D): C[T] = f(at)

given [T]: BifunctorK[K21.Id2[T]] with
def bimapK[A[_], B[_], C[_], D[_]](at: B[T])(f: A ~> C, g: B ~> D): D[T] = g(at)

given [T]: BifunctorK[K21.Const[T]] with
def bimapK[A[_], B[_], C[_], D[_]](t: T)(f: A ~> C, g: B ~> D): T = t

given [T]: BifunctorK[[f[_], g[_]] =>> Tuple2K[f, g, T]] with
def bimapK[A[_], B[_], C[_], D[_]](fab: Tuple2K[A, B, T])(f: A ~> C, g: B ~> D): Tuple2K[C, D, T] =
Tuple2K(f(fab.first), g(fab.second))

given [T]: BifunctorK[[f[_], g[_]] =>> EitherK[f, g, T]] with
def bimapK[A[_], B[_], C[_], D[_]](fab: EitherK[A, B, T])(f: A ~> C, g: B ~> D): EitherK[C, D, T] =
EitherK(fab.run match
case Left(a) => Left(f(a))
case Right(b) => Right(g(b))
)

given bifunctorKGen[F[_[_], _[_]]](using inst: => K21.Instances[BifunctorK, F]): BifunctorK[F] with
def bimapK[A[_], B[_], C[_], D[_]](fab: F[A, B])(f: A ~> C, g: B ~> D): F[C, D] =
inst.map(fab)([f[_[_], _[_]]] => (bf: BifunctorK[f], fab: f[A, B]) => bf.bimapK(fab)(f, g))

inline def derived[F[_[_], _[_]]: K21.Generic]: BifunctorK[F] = bifunctorKGen

case class Fix[S[_, _], A](unfix: S[A, Fix[S, A]])

trait Bifunctor[F[_, _]]:
Expand Down