Skip to content

Commit

Permalink
Fix FunctorK test example for recursive types (#194)
Browse files Browse the repository at this point in the history
Make instances in `functorKGen` by-name.
  • Loading branch information
joroKr21 authored Jan 10, 2024
1 parent 04af20c commit d6d82cd
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 68 deletions.
17 changes: 14 additions & 3 deletions modules/deriving/src/test/scala/shapeless3/deriving/adts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ package shapeless3.deriving
object adts:
case class ISB(i: Int, s: String, b: Boolean) derives Monoid, Eq, Empty, Show, Read

case class Box[A](x: A) derives Monoid, Eq, Show, Read, Functor, Pure, Ord, Traverse, Foldable
case class Box[A](x: A) derives Monoid, Eq, Show, Read, Functor, Return, Ord, Traverse, Foldable

case class Recursive(h: Int, t: Option[Recursive]) derives Monoid

sealed trait OptionInt derives Eq, Show, Read, Ord
case object NoneInt extends OptionInt
case class SomeInt(value: Int) extends OptionInt

sealed trait Opt[+A] derives Eq, Show, Read, Functor, EmptyK, Pure, Ord, Traverse, Foldable
sealed trait Opt[+A] derives Eq, Show, Read, Functor, EmptyK, Return, Ord, Traverse, Foldable
case object Nn extends Opt[Nothing]
case class Sm[+A](value: A) extends Opt[A]

Expand All @@ -53,8 +53,13 @@ object adts:
def fold: T = this match
case Given(t) => t
case Default(t) => t
def toOption: Option[T] = this match
case Given(t) => Some(t)
case Default(_) => None

object OptionD:
val fold: OptionD ~> Id = [t] => (ot: OptionD[t]) => ot.fold
val fold: OptionD ~> Id = [t] => (od: OptionD[t]) => od.fold
val toOption: OptionD ~> Option = [t] => (od: OptionD[t]) => od.toOption

case class Given[T](value: T) extends OptionD[T]
case class Default[T](value: T) extends OptionD[T]
Expand All @@ -78,3 +83,9 @@ object adts:
case Top(head: A, tail: Zipper[A])
case Bot(init: Zipper[A], last: A)
case Focus(left: List[A], focus: ::[A], right: List[A])

sealed trait HkNel[F[_]]
case class HkCons[F[_]](head: F[Int], tail: HkNel[F]) extends HkNel[F]
case class HkOne[F[_]](head: F[Int]) extends HkNel[F]

end adts
80 changes: 41 additions & 39 deletions modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object Inc:

class DerivationTests:
@Test
def monoid: Unit =
def monoid(): Unit =
val v0 = Monoid[ISB]
assert(v0.empty == ISB(0, "", false))
assert(v0.combine(ISB(1, "foo", false), ISB(2, "bar", true)) == ISB(3, "foobar", true))
Expand All @@ -60,7 +60,7 @@ class DerivationTests:
)

@Test
def eq: Unit =
def eq(): Unit =
val v0 = Eq[SomeInt]
assert(v0.eqv(SomeInt(23), SomeInt(23)))
assert(!v0.eqv(SomeInt(23), SomeInt(13)))
Expand Down Expand Up @@ -96,7 +96,7 @@ class DerivationTests:
assert(!v9.eqv(SmE(23), NnE))

@Test
def order: Unit =
def order(): Unit =
val v0 = Ord[Unit]
assert(v0.compare((), ()) == 0)

Expand Down Expand Up @@ -143,7 +143,7 @@ class DerivationTests:
assert(v8.compare(SmE("foo"), NnE) == 1)

@Test
def functor: Unit =
def functor(): Unit =
val v0 = Functor[Box]
assert(v0.map(Box("foo"))(_.length) == Box(3))

Expand Down Expand Up @@ -181,7 +181,7 @@ class DerivationTests:
loop(n, CNil)

@Test
def foldable: Unit =
def foldable(): Unit =
val v0 = Foldable[Box]
assert(v0.foldLeft(Box(1))(0)((acc: Int, x: Int) => acc + x) == 1)
assert(v0.foldRight(Box(1))(Eval.now(0))((x: Int, acc: Eval[Int]) => acc.map(_ + x)).value == 1)
Expand Down Expand Up @@ -218,7 +218,7 @@ class DerivationTests:
assert(v7.foldRight(NnE)(Eval.now(0))((x: Int, acc: Eval[Int]) => acc.map(_ + x)).value == 0)

@Test
def traverse: Unit =
def traverse(): Unit =
val v0 = Traverse[Box]
assert(v0.traverse(Box(1))((x: Int) => List(x + 1)) == List(Box(2)))

Expand All @@ -231,12 +231,12 @@ class DerivationTests:
assert(v3.traverse(Nn)((x: Int) => List(x + 1)) == List(Nn))

val v4 = Traverse[Const[CNil.type]]
assert(v4.traverse(CNil)(Option.apply) == Some(CNil))
assert(v4.traverse(CNil)(Option.apply).contains(CNil))
val v5 = Traverse[CCons]
assert(v5.traverse(CList("foo", "bar"))(Option.apply) == Some(CList("foo", "bar")))
assert(v5.traverse(CList("foo", "bar"))(Option.apply).contains(CList("foo", "bar")))
val v6 = Traverse[CList]
assert(v6.traverse(CList("foo", "bar"))(Option.apply) == Some(CList("foo", "bar")))
assert(v6.traverse(CNil)(Option.apply) == Some(CNil))
assert(v6.traverse(CList("foo", "bar"))(Option.apply).contains(CList("foo", "bar")))
assert(v6.traverse(CNil)(Option.apply).contains(CNil))
assert(v6.traverse(CList("foo", "bar"))(() => _).apply() == CList("foo", "bar"))
assert(v6.traverse(CList(1, 2))(x => List(x, x + 1)) == List(CList(1, 2), CList(1, 3), CList(2, 2), CList(2, 3)))

Expand All @@ -245,23 +245,26 @@ class DerivationTests:
assert(v7.traverse(NnE)((x: Int) => List(x + 1)) == List(NnE))

val v8 = Traverse[Phantom]
assert(v8.traverse(Phantom())(Option.apply) == Some(Phantom()))
assert(v8.traverse(Phantom())(Option.apply).contains(Phantom()))

@Test
def functork: Unit =
def functorK(): Unit =
val v0 = FunctorK[Order]
assert(v0.mapK(Order[OptionD](Given("Epoisse"), Default(10)))(OptionD.fold) == Order[Id]("Epoisse", 10))
val arg1 = HkCons(Given(42), HkCons(Default(0), HkOne(Given(313))))
val exp1 = HkCons(Some(42), HkCons(None, HkOne(Some(313))))
assert(FunctorK[HkNel].mapK(arg1)(OptionD.toOption) == exp1)

@Test
def bifunctor: Unit =
def bifunctor(): Unit =
val v0 = Bifunctor[ConsF]
val v1 = Bifunctor[ListF]
val v2: ListF.List[String] = Fix(ConsF("foo", Fix(ConsF("quux", Fix(ConsF("wibble", Fix(NilF)))))))
val v3: ListF.List[Int] = Fix(ConsF(3, Fix(ConsF(4, Fix(ConsF(6, Fix(NilF)))))))
assert(Bifunctor.map((_: String).length)(v2) == v3)

@Test
def data: Unit =
def data(): Unit =
val v0 = Data[Size.type, ISB, Int]
assert(v0.gmapQ(ISB(23, "foo", true)).sum == 27)
val v1 = Data[Size.type, OptionInt, Int]
Expand All @@ -271,7 +274,7 @@ class DerivationTests:
assert(v2.gmapQ(CCons("foo", CCons("quux", CCons("wibble", CNil)))).sum == 13)

@Test
def datat: Unit =
def dataT(): Unit =
val v0 = DataT[Inc.type, ISB]
assert(v0.gmapT(ISB(23, "foo", true)) == ISB(24, "foo!", false))
val v1 = DataT[Inc.type, OptionInt]
Expand All @@ -281,32 +284,32 @@ class DerivationTests:
assert(v2.gmapT(CCons(1, CCons(2, CCons(3, CNil)))) == CCons(2, CCons(3, CCons(4, CNil))))

@Test
def empty: Unit =
def empty(): Unit =
val v0 = Empty[ISB]
assert(v0.empty == ISB(0, "", false))

@Test
def emptyk: Unit =
def emptyK(): Unit =
val v0 = EmptyK[Opt]
assert(v0.empty[Int] == Nn)
val v1 = EmptyK[CList]
assert(v1.empty[Int] == CNil)

@Test
def pure: Unit =
val v0 = Pure[Box]
def pure(): Unit =
val v0 = Return[Box]
assert(v0.pure(23) == Box(23))
val v1 = Pure[CList]
val v1 = Return[CList]
assert(v1.pure(23) == CCons(23, CNil))

@Test
def labels: Unit =
def labels(): Unit =
val v0 = K0.Generic[ISB]
val v1 = constValueTuple[v0.MirroredElemLabels]
assert(v1 == ("i", "s", "b"))

@Test
def show: Unit =
def show(): Unit =
val v0 = Show[ISB]
assert(v0.show(ISB(23, "foo", true)) == """ISB(i: 23, s: "foo", b: true)""")

Expand All @@ -323,7 +326,7 @@ class DerivationTests:

val v4 = Show[CList[Int]]
assert(
v4.show((CCons(1, CCons(2, CCons(3, CNil))))) == "CCons(hd: 1, tl: CCons(hd: 2, tl: CCons(hd: 3, tl: CNil)))"
v4.show(CCons(1, CCons(2, CCons(3, CNil)))) == "CCons(hd: 1, tl: CCons(hd: 2, tl: CCons(hd: 3, tl: CNil)))"
)

val v5 = Show[Order[Id]]
Expand All @@ -334,42 +337,41 @@ class DerivationTests:
assert(v6.show(NnE) == "NnE")

@Test
def read: Unit =
def read(): Unit =
val v0 = Read[ISB]
assert(v0.read("""ISB(i: 23, s: "foo", b: true)""") == Some((ISB(23, "foo", true), "")))
assert(v0.read("""ISB(i: 23, s: "foo", b: true)""").contains((ISB(23, "foo", true), "")))

val v1 = Read[OptionInt]
assert(v1.read("SomeInt(value: 23)") == Some((SomeInt(23), "")))
assert(v1.read("NoneInt") == Some((NoneInt, "")))
assert(v1.read("SomeInt(value: 23)").contains((SomeInt(23), "")))
assert(v1.read("NoneInt").contains((NoneInt, "")))

val v2 = Read[Box[Int]]
assert(v2.read("Box(x: 23)") == Some((Box(23), "")))
assert(v2.read("Box(x: 23)").contains((Box(23), "")))

val v3 = Read[Opt[Int]]
assert(v3.read("Sm(value: 23)") == Some((Sm(23), "")))
assert(v3.read("Nn") == Some((Nn, "")))
assert(v3.read("Sm(value: 23)").contains((Sm(23), "")))
assert(v3.read("Nn").contains((Nn, "")))

val v4 = Read[CList[Int]]
assert(
v4.read("CCons(hd: 1, tl: CCons(hd: 2, tl: CCons(hd: 3, tl: CNil)))") == Some(
(CCons(1, CCons(2, CCons(3, CNil))), "")
)
v4.read("CCons(hd: 1, tl: CCons(hd: 2, tl: CCons(hd: 3, tl: CNil)))")
.contains((CCons(1, CCons(2, CCons(3, CNil))), ""))
)

val v5 = Read[Order[Id]]
assert(v5.read("""Order(item: "Epoisse", quantity: 10)""") == Some((Order[Id]("Epoisse", 10), "")))
assert(v5.read("""Order(item: "Epoisse", quantity: 10)""").contains((Order[Id]("Epoisse", 10), "")))

val v6 = Read[OptE[Int]]
assert(v6.read("SmE(value: 23)") == Some((SmE(23), "")))
assert(v6.read("NnE") == Some((NnE, "")))
assert(v6.read("SmE(value: 23)").contains((SmE(23), "")))
assert(v6.read("NnE").contains((NnE, "")))

@Test
def transform: Unit =
def transform(): Unit =
val v0 = Transform[BI, ISB]
assert(v0(BI(true, 23)) == ISB(23, "", true))

@Test
def repr: Unit =
def repr(): Unit =
val v0 = K0.ProductGeneric[Box[Int]]
val v1 = v0.toRepr(Box(23))
val v1a: Tuple1[Int] = v1
Expand All @@ -381,7 +383,7 @@ class DerivationTests:
assert(v3 == ("Epoisse", 10))

@Test
def parsing: Unit =
def parsing(): Unit =
val parser = Parser[ISB]
// Applicative
assertEquals(Right(ISB(42, "foo", true)), parser.parseAccum("s=foo,i=42,b=true,hidden=?"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ object Optional:
def headOption[A](fa: Option[A]): Option[A] = fa
given [T]: Optional[Const[T]] with
def headOption[A](fa: T): Option[A] = None
given NonEmpty[::] with
given cons: NonEmpty[::] with
def head[A](fa: ::[A]): A = fa.head
given NonEmpty[Some] with
def head[A](fa: Some[A]): A = fa.get
Expand Down Expand Up @@ -333,7 +333,7 @@ object FunctorK:
given [T]: FunctorK[K11.Id[T]] with
def mapK[A[_], B[_]](at: A[T])(f: A ~> B): B[T] = f(at)

given functorKGen[H[_[_]]](using inst: K11.Instances[FunctorK, H]): FunctorK[H] with
given functorKGen[H[_[_]]](using inst: => K11.Instances[FunctorK, H]): FunctorK[H] with
def mapK[A[_], B[_]](ha: H[A])(f: A ~> B): H[B] =
inst.map(ha)([t[_[_]]] => (ft: FunctorK[t], ta: t[A]) => ft.mapK(ta)(f))

Expand Down Expand Up @@ -458,10 +458,10 @@ object Empty:
given Empty[Boolean] = mkEmpty(false)

given emptyGen[A](using inst: K0.ProductInstances[Empty, A]): Empty[A] =
mkEmpty(inst.construct([a] => (_: Empty[a]).empty))
mkEmpty(inst.construct([a] => (A: Empty[a]) => A.empty))

inline given emptyGenC[A](using gen: K0.CoproductGeneric[A]): Empty[A] =
mkEmpty(gen.withOnly[Empty, A]([a <: A] => (_: Empty[a]).empty))
mkEmpty(gen.withOnly[Empty, A]([a <: A] => (A: Empty[a]) => A.empty))

inline def derived[A](using gen: K0.Generic[A]): Empty[A] =
inline gen match
Expand All @@ -481,11 +481,11 @@ object EmptyK:
new EmptyK[F]:
def empty[A] = f[A]()

given emptyKGen[A[_]](using inst: K1.ProductInstances[EmptyK, A]): EmptyK[A] =
mkEmptyK([t] => () => inst.construct([f[_]] => (_: EmptyK[f]).empty[t]))
given emptyKGen[F[_]](using inst: K1.ProductInstances[EmptyK, F]): EmptyK[F] =
mkEmptyK([t] => () => inst.construct([f[_]] => (F: EmptyK[f]) => F.empty[t]))

inline given emptyKGenC[A[_]](using gen: K1.CoproductGeneric[A]): EmptyK[A] =
mkEmptyK[A]([t] => () => gen.withOnly[EmptyK, A[t]]([a[x] <: A[x]] => (_: EmptyK[a]).empty[t]))
inline given emptyKGenC[F[_]](using gen: K1.CoproductGeneric[F]): EmptyK[F] =
mkEmptyK[F]([t] => () => gen.withOnly[EmptyK, F[t]]([f[x] <: F[x]] => (F: EmptyK[f]) => F.empty[t]))

inline def derived[A[_]](using gen: K1.Generic[A]): EmptyK[A] =
inline gen match
Expand All @@ -509,30 +509,27 @@ object Alt1:
case gt: G[T] => new Alt1G(gt)
}

trait Pure[F[_]]:
trait Return[F[_]]:
def pure[A](a: A): F[A]

object Pure:
def apply[F[_]](using ef: Pure[F]): Pure[F] = ef
object Return:
def apply[F[_]](using F: Return[F]): Return[F] = F

def mkPure[F[_]](f: [a] => a => F[a]): Pure[F] =
new Pure[F]:
def pure[A](a: A) = f(a)
def from[F[_]](f: [a] => a => F[a]): Return[F] = new Return[F]:
def pure[A](a: A) = f(a)

given Pure[Id] = mkPure([T] => (t: T) => t)
given Return[Id] =
from([T] => (t: T) => t)

given pureGen[A[_]](using inst: K1.ProductInstances[Alt1.Of[Pure, EmptyK], A]): Pure[A] =
mkPure[A](
[t] => (a: t) => inst.construct([f[_]] => (af: Alt1.Of[Pure, EmptyK][f]) => af.fold[f[t]](_.pure(a))(_.empty[t]))
)
given pureGen[A[_]](using inst: K1.ProductInstances[Alt1.Of[Return, EmptyK], A]): Return[A] = from[A]:
[t] => (a: t) => inst.construct([f[_]] => (af: Alt1.Of[Return, EmptyK][f]) => af.fold[f[t]](_.pure(a))(_.empty[t]))

inline given pureGenC[A[_]](using gen: K1.CoproductGeneric[A]): Pure[A] =
mkPure[A]([t] => (a: t) => gen.withFirst[Pure, A[t]]([f[x] <: A[x]] => (_: Pure[f]).pure(a)))
inline given pureGenC[F[_]](using gen: K1.CoproductGeneric[F]): Return[F] = from[F]:
[t] => (a: t) => gen.withFirst[Return, F[t]]([f[x] <: F[x]] => (F: Return[f]) => F.pure(a))

inline def derived[A[_]](using gen: K1.Generic[A]): Pure[A] =
inline gen match
case given K1.ProductGeneric[A] => pureGen
case given K1.CoproductGeneric[A] => pureGenC
inline def derived[A[_]](using gen: K1.Generic[A]): Return[A] = inline gen match
case given K1.ProductGeneric[A] => pureGen
case given K1.CoproductGeneric[A] => pureGenC

trait Show[T]:
def show(t: T): String
Expand Down Expand Up @@ -587,7 +584,7 @@ object Read:
yield (p, tl)

given Read[Int] = readPrimitive("""(-?\d*)(.*)""".r, s => Try(s.toInt).toOption)
given Read[String] = (s: String) => head(s, """\"(.*)\"(.*)""".r)
given Read[String] = (s: String) => head(s, """"(.*)"(.*)""".r)
given Read[Boolean] = readPrimitive("""(true|false)(.*)""".r, s => Try(s.toBoolean).toOption)

given readGen[T](using inst: K0.ProductInstances[Read, T], labelling: Labelling[T]): Read[T] with
Expand Down

0 comments on commit d6d82cd

Please sign in to comment.