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 functionality to construct products in applicative / monadic way #101

Merged
merged 10 commits into from
Sep 16, 2022
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ lazy val local = crossProject(JSPlatform, JVMPlatform, NativePlatform)

lazy val commonSettings = Seq(
scalacOptions ++= Seq("-Xfatal-warnings", "-Yexplicit-nulls", "-deprecation"),
Test / scalacOptions -= "-Yexplicit-nulls",
Compile / doc / sources := Nil,
libraryDependencies += "com.github.sbt" % "junit-interface" % "0.13.3" % "test",
testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-s", "-v"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,17 @@ import scala.reflect.ClassTag

type Id[t] = t
type Const[c] = [t] =>> c

type ~>[A[_], B[_]] = [t] => A[t] => B[t]

/** Corresponds to `Applicative.pure` in Cats. */
type Pure[F[_]] = [a] => a => F[a]
/** Corresponds to `Functor.map` in Cats. */
type MapF[F[_]] = [a, b] => (F[a], a => b) => F[b]
/** Corresponds to `Apply.ap` in Cats. */
type Ap[F[_]] = [a, b] => (F[a => b], F[a]) => F[b]
/** Corresponds to `FlatMap.tailRecM` in Cats. */
type TailRecM[F[_]] = [a, b] => (a, a => F[Either[a, b]]) => F[b]

inline def summonAsArray[T <: Tuple]: Array[Any] =
summonAsArray0[T](0, new Array[Any](constValue[Tuple.Size[T]]))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ import shapeless3.deriving.*
private[shapeless3] abstract class ErasedInstances[K, FT] extends Serializable {
def erasedMapK(f: Any => Any): ErasedInstances[K, ?]
def erasedMap(x: Any)(f: (Any, Any) => Any): Any
def erasedTraverse(x0: Any)(map: (Any, Any) => Any)(pure: Any => Any)(ap: (Any, Any) => Any)(f: (Any, Any) => Any): Any
def erasedTraverse[F[_]](x: Any)(map: MapF[F])(pure: Pure[F])(ap: Ap[F])(f: (Any, Any) => F[Any]): F[Any]
}

private[shapeless3] abstract class ErasedProductInstances[K, FT] extends ErasedInstances[K, FT] {
def erasedMapK(f: Any => Any): ErasedProductInstances[K, _]
def erasedConstruct(f: Any => Any): Any
def erasedConstructA[F[_]](f: Any => F[Any])(pure: Pure[F], map: MapF[F], ap: Ap[F]): F[Any]
def erasedConstructM[F[_]](f: Any => F[Any])(pure: Pure[F], map: MapF[F], tailRecM: TailRecM[F]): F[Any]
def erasedUnfold(a: Any)(f: (Any, Any) => (Any, Option[Any])): (Any, Option[Any])
def erasedMap(x0: Any)(f: (Any, Any) => Any): Any
def erasedMap2(x0: Any, y0: Any)(f: (Any, Any, Any) => Any): Any
Expand All @@ -49,12 +51,20 @@ private[shapeless3] final class ErasedProductInstances1[K, FT](val mirror: Mirro
lazy val i = i0()

inline def toProduct(x: Any): Product = x.asInstanceOf[Product]
private def toElement(x: Any) = toProduct(x).productElement(0)
private def fromElement(x: Any) = mirror.fromProduct(Tuple1(x))

final def erasedMapK(f: Any => Any): ErasedProductInstances[K, ?] =
new ErasedProductInstances1(mirror, () => f(i))

final def erasedConstruct(f: Any => Any): Any =
mirror.fromProduct(Tuple1(f(i)))
fromElement(f(i))

final def erasedConstructA[F[_]](f: Any => F[Any])(pure: Pure[F], map: MapF[F], ap: Ap[F]): F[Any] =
map(f(i), fromElement)

final def erasedConstructM[F[_]](f: Any => F[Any])(pure: Pure[F], map: MapF[F], tailRecM: TailRecM[F]): F[Any] =
map(f(i), fromElement)

final def erasedUnfold(a: Any)(f: (Any, Any) => (Any, Option[Any])): (Any, Option[Any]) = {
val (acc0, e0) = f(a, i)
Expand All @@ -64,45 +74,45 @@ private[shapeless3] final class ErasedProductInstances1[K, FT](val mirror: Mirro
}
}

final def erasedMap(x0: Any)(f: (Any, Any) => Any): Any =
mirror.fromProduct(Tuple1(f(i, toProduct(x0).productElement(0))))
final def erasedMap(x: Any)(f: (Any, Any) => Any): Any =
fromElement(f(i, toElement(x)))

final def erasedTraverse(x0: Any)(map: (Any, Any) => Any)(pure: Any => Any)(ap: (Any, Any) => Any)(f: (Any, Any) => Any) =
map(f(i, toProduct(x0).productElement(0)), (x: Any) => mirror.fromProduct(Tuple1(x)))
final def erasedTraverse[F[_]](x: Any)(map: MapF[F])(pure: Pure[F])(ap: Ap[F])(f: (Any, Any) => F[Any]): F[Any] =
map(f(i, toElement(x)), fromElement)

final def erasedMap2(x0: Any, y0: Any)(f: (Any, Any, Any) => Any): Any =
mirror.fromProduct(Tuple1(f(i, toProduct(x0).productElement(0), toProduct(y0).productElement(0))))
final def erasedMap2(x: Any, y: Any)(f: (Any, Any, Any) => Any): Any =
fromElement(f(i, toElement(x), toElement(y)))

final def erasedFoldLeft(x0: Any)(a: Any)(f: (Any, Any, Any) => CompleteOr[Any]): Any = {
f(a, i, toProduct(x0).productElement(0)) match {
final def erasedFoldLeft(x: Any)(a: Any)(f: (Any, Any, Any) => CompleteOr[Any]): Any = {
f(a, i, toElement(x)) match {
case Complete(r) => r
case acc => acc
}
}

final def erasedFoldLeft2(x0: Any, y0: Any)(a: Any)(f: (Any, Any, Any, Any) => CompleteOr[Any]): Any = {
f(a, i, toProduct(x0).productElement(0), toProduct(y0).productElement(0)) match {
final def erasedFoldLeft2(x: Any, y: Any)(a: Any)(f: (Any, Any, Any, Any) => CompleteOr[Any]): Any = {
f(a, i, toElement(x), toElement(y)) match {
case Complete(r) => r
case acc => acc
}
}

final def erasedFoldRight(x0: Any)(a: Any)(f: (Any, Any, Any) => CompleteOr[Any]): Any = {
f(i, toProduct(x0).productElement(0), a) match {
final def erasedFoldRight(x: Any)(a: Any)(f: (Any, Any, Any) => CompleteOr[Any]): Any = {
f(i, toElement(x), a) match {
case Complete(r) => r
case acc => acc
}
}

final def erasedFoldRight2(x0: Any, y0: Any)(a: Any)(f: (Any, Any, Any, Any) => CompleteOr[Any]): Any = {
f(i, toProduct(x0).productElement(0), toProduct(y0).productElement(0), a) match {
final def erasedFoldRight2(x: Any, y: Any)(a: Any)(f: (Any, Any, Any, Any) => CompleteOr[Any]): Any = {
f(i, toElement(x), toElement(y), a) match {
case Complete(r) => r
case acc => acc
}
}

final def erasedProject(x0: Any)(p: Int)(f: (Any, Any) => Any): Any =
f(i, toProduct(x0).productElement(0))
final def erasedProject(x: Any)(p: Int)(f: (Any, Any) => Any): Any =
f(i, toElement(x))
}

object ErasedProductInstances1 {
Expand All @@ -111,36 +121,63 @@ object ErasedProductInstances1 {
}

private[shapeless3] final class ErasedProductInstancesN[K, FT](val mirror: Mirror.Product, is0: () => Array[Any]) extends ErasedProductInstances[K, FT] {
import ErasedProductInstances._

@deprecated("Preserved for bincompat reasons. DO NOT USE as it will lead to stack overflows when deriving instances for recursive types")
def this(mirror: Mirror.Product, is0: Array[Any]) = this(mirror, () => is0)

lazy val is: Array[Any] = is0()

import ErasedProductInstances.ArrayProduct

inline def toProduct(x: Any): Product = x.asInstanceOf[Product]
private def fromArray(xs: Array[Any]) = mirror.fromProduct(new ArrayProduct(xs))
private def fromIndexedSeq(xs: IndexedSeq[Any]) = mirror.fromProduct(new IndexedSeqProduct(xs))
private def fromEmptyProduct = mirror.fromProduct(None)

private def traverseProduct[F[_]](x: Product, f: (Any, Any) => F[Any])(pure: Pure[F], map: MapF[F], ap: Ap[F]): F[Any] =
val n = is.length
if n == 0 then pure(fromEmptyProduct)
else
var acc = pure(Vector.empty[Any])
var i = 0
while i < n do
acc = ap(map(acc, _.appended), f(is(i), x.productElement(i)))
i += 1
map(acc, fromIndexedSeq)
end traverseProduct

final def erasedMapK(f: Any => Any): ErasedProductInstances[K, ?] =
new ErasedProductInstancesN(mirror, () => is.map(f))

final def erasedConstruct(f: Any => Any): Any = {
val n = is.length
if (n == 0) mirror.fromProduct(None)
if (n == 0) fromEmptyProduct
else {
val arr = new Array[Any](n)
var i = 0
while(i < n) {
arr(i) = f(is(i))
i = i+1
}
mirror.fromProduct(new ArrayProduct(arr))
fromArray(arr)
}
}

final def erasedConstructA[F[_]](f: Any => F[Any])(pure: Pure[F], map: MapF[F], ap: Ap[F]): F[Any] =
traverseProduct(new ArrayProduct(is), (tc, _) => f(tc))(pure, map, ap)

final def erasedConstructM[F[_]](f: Any => F[Any])(pure: Pure[F], map: MapF[F], tailRecM: TailRecM[F]): F[Any] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just a stacksafe version of constructA or is there some other difference that I've missed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constructA is based on Applicative:

  • It can be parallel
  • It cannot short circuit (evaluates for all fields)
  • It is not stack safe (currently) - but it could be optimized if we build a tree instead of a list of ap

constructM is based on Monad:

  • It is always sequential
  • It can short circuit
  • It is stack safe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cats.Applicative actually has map2Eval which can short circuit but that's too much complexity for Shapeless

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TimWSpence here is a failed attempt to make traverse and constructA stack safe: #103
But it doesn't actually build a tree ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just saw this. Will have a look!

val n = is.length
def step(xs: Vector[Any]) =
val i = xs.length
if i >= n then pure(Right(fromIndexedSeq(xs): Any))
else map(f(is(i)), a => Left(xs :+ a))
if n == 0 then pure(fromEmptyProduct)
else tailRecM(Vector.empty, step)
end erasedConstructM

final def erasedUnfold(a: Any)(f: (Any, Any) => (Any, Option[Any])): (Any, Option[Any]) = {
val n = is.length
if (n == 0) (a, Some(mirror.fromProduct(None)))
if (n == 0) (a, Some(fromEmptyProduct))
else {
val arr = new Array[Any](n)
var acc = a
Expand All @@ -156,7 +193,7 @@ private[shapeless3] final class ErasedProductInstancesN[K, FT](val mirror: Mirro
}
i = i+1
}
(acc, Some(mirror.fromProduct(new ArrayProduct(arr))))
(acc, Some(fromArray(arr)))
}
}

Expand All @@ -171,33 +208,12 @@ private[shapeless3] final class ErasedProductInstancesN[K, FT](val mirror: Mirro
arr(i) = f(is(i), x.productElement(i))
i = i+1
}
mirror.fromProduct(new ArrayProduct(arr))
fromArray(arr)
}
}

final def erasedTraverse(x0: Any)(map: (Any, Any) => Any)(pure: Any => Any)(ap: (Any, Any) => Any)(f: (Any, Any) => Any) = {
val n = is.length

def prepend(xs: List[Any])(x: Any) = x :: xs
def fromList(xs: List[Any]) =
val arr = new Array[Any](n)
@tailrec def toProduct(xs: List[Any], i: Int): Product = xs match
case x :: xs => arr(i) = x; toProduct(xs, i - 1)
case Nil => new ArrayProduct(arr)
mirror.fromProduct(toProduct(xs, n - 1))

if (n == 0) pure(x0)
else {
val x = toProduct(x0)
var acc = pure(Nil)
var i = 0
while(i < n) {
acc = ap(map(acc, prepend), f(is(i), x.productElement(i)))
i = i+1
}
map(acc, fromList)
}
}
final def erasedTraverse[F[_]](x: Any)(map: MapF[F])(pure: Pure[F])(ap: Ap[F])(f: (Any, Any) => F[Any]): F[Any] =
traverseProduct(toProduct(x), f)(pure, map, ap)

final def erasedMap2(x0: Any, y0: Any)(f: (Any, Any, Any) => Any): Any = {
val n = is.length
Expand All @@ -211,7 +227,7 @@ private[shapeless3] final class ErasedProductInstancesN[K, FT](val mirror: Mirro
arr(i) = f(is(i), x.productElement(i), y.productElement(i))
i = i+1
}
mirror.fromProduct(new ArrayProduct(arr))
fromArray(arr)
}
}

Expand Down Expand Up @@ -305,8 +321,15 @@ object ErasedProductInstancesN {
private[shapeless3] object ErasedProductInstances {
class ArrayProduct(val elems: Array[Any]) extends Product {
def canEqual(that: Any): Boolean = true
def productElement(n: Int) = elems(n)
def productArity = elems.length
def productElement(n: Int): Any = elems(n)
def productArity: Int = elems.length
override def productIterator: Iterator[Any] = elems.iterator
}

final class IndexedSeqProduct(elems: IndexedSeq[Any]) extends Product {
def canEqual(that: Any): Boolean = true
def productElement(n: Int): Any = elems(n)
def productArity: Int = elems.length
override def productIterator: Iterator[Any] = elems.iterator
}

Expand Down Expand Up @@ -348,10 +371,8 @@ private[shapeless3] final class ErasedCoproductInstances[K, FT](mirror: Mirror.S
f(i, x)
}

final def erasedTraverse(x: Any)(map: (Any, Any) => Any)(pure: Any => Any)(ap: (Any, Any) => Any)(f: (Any, Any) => Any): Any = {
val i = ordinal(x)
f(i, x)
}
final def erasedTraverse[F[_]](x: Any)(map: MapF[F])(pure: Pure[F])(ap: Ap[F])(f: (Any, Any) => F[Any]): F[Any] =
f(ordinal(x), x)

final def erasedFold2(x: Any, y: Any)(a: => Any)(f: (Any, Any, Any) => Any): Any = {
val i = mirror.ordinal(x.asInstanceOf)
Expand Down
35 changes: 26 additions & 9 deletions modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,20 @@ object K0 {
inst.erasedMapK(f.asInstanceOf).asInstanceOf
inline def map(x: T)(f: [t] => (F[t], t) => t): T =
inst.erasedMap(x)(f.asInstanceOf).asInstanceOf
inline def widen[G[t] >: F[t]]: Instances[G, T] = inst.asInstanceOf
inline def traverse[G[_]](x: T)(map: [a,b] => (G[a], (a => b)) => G[b])(pure: [a] => a => G[a])(ap: [a,b] => (G[a => b], G[a]) => G[b])(k: [t] => (F[t], t) => G[t]): G[T] =
inst.erasedTraverse(x)(map.asInstanceOf)(pure.asInstanceOf)(ap.asInstanceOf)(k.asInstanceOf).asInstanceOf
inline def widen[G[t] >: F[t]]: Instances[G, T] =
inst.asInstanceOf
inline def traverse[G[_]](x: T)(map: MapF[G])(pure: Pure[G])(ap: Ap[G])(f: [t] => (F[t], t) => G[t]): G[T] =
inst.erasedTraverse(x)(map)(pure)(ap)(f.asInstanceOf).asInstanceOf

extension [F[_], T](inst: ProductInstances[F, T])
inline def mapK[G[_]](f: [t] => F[t] => G[t]): ProductInstances[G, T] =
inst.erasedMapK(f.asInstanceOf).asInstanceOf
inline def construct(f: [t] => F[t] => t): T =
inst.erasedConstruct(f.asInstanceOf).asInstanceOf
inline def constructA[G[_]](f: [t] => F[t] => G[t])(pure: Pure[G], map: MapF[G], ap: Ap[G]): G[T] =
inst.erasedConstructA(f.asInstanceOf)(pure, map, ap).asInstanceOf
inline def constructM[G[_]](f: [t] => F[t] => G[t])(pure: Pure[G], map: MapF[G], tailRecM: TailRecM[G]): G[T] =
inst.erasedConstructM(f.asInstanceOf)(pure, map, tailRecM).asInstanceOf
inline def map2(x: T, y: T)(f: [t] => (F[t], t, t) => t): T =
inst.erasedMap2(x, y)(f.asInstanceOf).asInstanceOf
inline def unfold[Acc](i: Acc)(f: [t] => (Acc, F[t]) => (Acc, Option[t])): (Acc, Option[T]) =
Expand Down Expand Up @@ -245,14 +250,18 @@ object K1 {
inst.erasedMap(x)(f.asInstanceOf).asInstanceOf
inline def widen[G[t[_]] >: F[t]]: Instances[G, T] =
inst.asInstanceOf
inline def traverse[A, G[_], R](x: T[A])(map: [a,b] => (G[a], (a => b)) => G[b])(pure: [a] => a => G[a])(ap: [a,b] => (G[a => b], G[a]) => G[b])(k: [t[_]] => (F[t], t[A]) => G[t[R]]): G[T[R]] =
inst.erasedTraverse(x)(map.asInstanceOf)(pure.asInstanceOf)(ap.asInstanceOf)(k.asInstanceOf).asInstanceOf
inline def traverse[A, G[_], R](x: T[A])(map: MapF[G])(pure: Pure[G])(ap: Ap[G])(f: [t[_]] => (F[t], t[A]) => G[t[R]]): G[T[R]] =
inst.erasedTraverse(x)(map)(pure)(ap)(f.asInstanceOf).asInstanceOf

extension [F[_[_]], T[_]](inst: ProductInstances[F, T])
inline def mapK[G[_[_]]](f: [t[_]] => F[t] => G[t]): ProductInstances[G, T] =
inst.erasedMapK(f.asInstanceOf).asInstanceOf
inline def construct[R](f: [t[_]] => F[t] => t[R]): T[R] =
inst.erasedConstruct(f.asInstanceOf).asInstanceOf
inline def constructA[G[_], R](f: [t[_]] => F[t] => G[t[R]])(pure: Pure[G], map: MapF[G], ap: Ap[G]): G[T[R]] =
inst.erasedConstructA(f.asInstanceOf)(pure, map, ap).asInstanceOf
inline def constructM[G[_], R](f: [t[_]] => F[t] => G[t[R]])(pure: Pure[G], map: MapF[G], tailRecM: TailRecM[G]): G[T[R]] =
inst.erasedConstructM(f.asInstanceOf)(pure, map, tailRecM).asInstanceOf
inline def map2[A, B, R](x: T[A], y: T[B])(f: [t[_]] => (F[t], t[A], t[B]) => t[R]): T[R] =
inst.erasedMap2(x, y)(f.asInstanceOf).asInstanceOf
inline def foldLeft[A, Acc](x: T[A])(i: Acc)(f: [t[_]] => (Acc, F[t], t[A]) => CompleteOr[Acc]): Acc =
Expand Down Expand Up @@ -374,14 +383,18 @@ object K11 {
inst.erasedMap(x)(f.asInstanceOf).asInstanceOf
inline def widen[G[t[_[_]]] >: F[t]]: Instances[G, T] =
inst.asInstanceOf
inline def traverse[A[_], G[_], R[_]](x: T[A])(map: [a,b] => (G[a], (a => b)) => G[b])(pure: [a] => a => G[a])(ap: [a,b] => (G[a => b], G[a]) => G[b])(k: [t[_[_]]] => (F[t], t[A]) => G[t[R]]): G[T[R]] =
inst.erasedTraverse(x)(map.asInstanceOf)(pure.asInstanceOf)(ap.asInstanceOf)(k.asInstanceOf).asInstanceOf
inline def traverse[A[_], G[_], R[_]](x: T[A])(map: MapF[G])(pure: Pure[G])(ap: Ap[G])(f: [t[_[_]]] => (F[t], t[A]) => G[t[R]]): G[T[R]] =
inst.erasedTraverse(x)(map)(pure)(ap)(f.asInstanceOf).asInstanceOf

extension [F[_[_[_]]], T[_[_]]](inst: ProductInstances[F, T])
inline def mapK[G[_[_[_]]]](f: [t[_[_]]] => F[t] => G[t]): ProductInstances[G, T] =
inst.erasedMapK(f.asInstanceOf).asInstanceOf
inline def construct[R[_]](f: [t[_[_]]] => F[t] => t[R]): T[R] =
inst.erasedConstruct(f.asInstanceOf).asInstanceOf
inline def constructA[G[_], R[_]](f: [t[_[_]]] => F[t] => G[t[R]])(pure: Pure[G], map: MapF[G], ap: Ap[G]): G[T[R]] =
inst.erasedConstructA(f.asInstanceOf)(pure, map, ap).asInstanceOf
inline def constructM[G[_], R[_]](f: [t[_[_]]] => F[t] => G[t[R]])(pure: Pure[G], map: MapF[G], tailRecM: TailRecM[G]): G[T[R]] =
inst.erasedConstructM(f.asInstanceOf)(pure, map, tailRecM).asInstanceOf
inline def map2[A[_], B[_], R[_]](x: T[A], y: T[B])(f: [t[_[_]]] => (F[t], t[A], t[B]) => t[R]): T[R] =
inst.erasedMap2(x, y)(f.asInstanceOf).asInstanceOf
inline def foldLeft[A[_], Acc](x: T[A])(i: Acc)(f: [t[_[_]]] => (Acc, F[t], t[A]) => CompleteOr[Acc]): Acc =
Expand Down Expand Up @@ -504,14 +517,18 @@ object K2 {
inst.erasedMap(x)(f.asInstanceOf).asInstanceOf
inline def widen[G[t[_, _]] >: F[t]]: Instances[G, T] =
inst.asInstanceOf
inline def traverse[A, B, G[_], R, S](x: T[A, B])(map: [a,b] => (G[a], (a => b)) => G[b])(pure: [a] => a => G[a])(ap: [a,b] => (G[a => b], G[a]) => G[b])(k: [t[_, _]] => (F[t], t[A, B]) => G[t[R, S]]): G[T[R, S]] =
inst.erasedTraverse(x)(map.asInstanceOf)(pure.asInstanceOf)(ap.asInstanceOf)(k.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 mapK[G[_[_, _]]](f: [t[_, _]] => F[t] => G[t]): ProductInstances[G, T] =
inst.erasedMapK(f.asInstanceOf).asInstanceOf
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[C, D]) => 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 =
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 @@ -16,6 +16,7 @@

package shapeless3.deriving

import org.junit.Assert.*
import org.junit.Test

import scala.annotation.tailrec
Expand Down Expand Up @@ -385,4 +386,16 @@ class DerivationTests {
val v3a: (String, Int) = v3
assert(v3 == ("Epoisse", 10))
}

@Test
def parsing: Unit =
val parser = Parser[ISB]
// Applicative
assertEquals(Right(ISB(42, "foo", true)), parser.parseAccum("s=foo,i=42,b=true,hidden=?"))
assertEquals(Left("Missing field 's';Invalid Boolean 'kinda';"), parser.parseAccum("i=42,b=kinda"))
assertEquals(Left("Invalid field 'broken';Invalid field '?';"), parser.parseAccum("i=42,broken,?"))
// Monadic
assertEquals(Right(ISB(42, "foo", true)), parser.parseShort("s=foo,i=42,b=true,hidden=?"))
assertEquals(Left("Missing field 's';"), parser.parseShort("i=42,b=kinda"))
assertEquals(Left("Invalid field 'broken';"), parser.parseShort("i=42,broken,?"))
}
Loading