Skip to content

Commit

Permalink
Add K21 test example with BifunctorK type class (#224)
Browse files Browse the repository at this point in the history
* Add K21 for BifunctorK

* Add tests for BifunctorK

* Move K21 to tests
  • Loading branch information
joroKr21 authored Aug 21, 2024
1 parent e961658 commit 9c39c49
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 1 deletion.
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

0 comments on commit 9c39c49

Please sign in to comment.