From 0100f70243f44f93ebe8861db03c203fb0688cd1 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Sat, 11 May 2024 17:34:56 +0200 Subject: [PATCH] Add tests for BifunctorK --- .../scala/shapeless3/deriving/kinds.scala | 4 +++ .../test/scala/shapeless3/deriving/adts.scala | 8 +++++ .../scala/shapeless3/deriving/deriving.scala | 13 ++++++++ .../shapeless3/deriving/type-classes.scala | 33 +++++++++++++++++-- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala b/modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala index 55868e6..40650ee 100644 --- a/modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala +++ b/modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala @@ -400,6 +400,10 @@ object K21 [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]] diff --git a/modules/deriving/src/test/scala/shapeless3/deriving/adts.scala b/modules/deriving/src/test/scala/shapeless3/deriving/adts.scala index 9f22b98..acc340b 100644 --- a/modules/deriving/src/test/scala/shapeless3/deriving/adts.scala +++ b/modules/deriving/src/test/scala/shapeless3/deriving/adts.scala @@ -16,6 +16,8 @@ package shapeless3.deriving +import cats.data.{EitherK, Tuple2K} + // ADTs object adts: @@ -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 diff --git a/modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala b/modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala index 09dc54e..26f77fb 100644 --- a/modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala +++ b/modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala @@ -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.* @@ -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] diff --git a/modules/deriving/src/test/scala/shapeless3/deriving/type-classes.scala b/modules/deriving/src/test/scala/shapeless3/deriving/type-classes.scala index 18b2057..196d431 100644 --- a/modules/deriving/src/test/scala/shapeless3/deriving/type-classes.scala +++ b/modules/deriving/src/test/scala/shapeless3/deriving/type-classes.scala @@ -18,8 +18,8 @@ package shapeless3.deriving import scala.annotation.tailrec import scala.compiletime.* - import cats.Eval +import cats.data.{EitherK, Tuple2K} // Type classes @@ -343,7 +343,36 @@ object FunctorK: inline def derived[F[_[_]]](using gen: K11.Generic[F]): FunctorK[F] = functorKGen trait BifunctorK[F[_[_], _[_]]]: - def mapK[A[_], B[_], C[_], D[_]](fab: F[A, B])(f: A ~> C, g: B ~> D): F[C, D] + 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]])