Refactor AList to handle K[F[a]]

We do need to treat AList like a typeclass after all because
sbt.std.Transform.uniform needs to traverse over a List,
and we won't be able to magically connvert a List into a tuple.
This commit is contained in:
Eugene Yokota 2022-01-03 01:25:43 -05:00
parent 7d33a5949e
commit c86fc37ce1
3 changed files with 65 additions and 56 deletions

View File

@ -14,31 +14,31 @@ import Types._
/**
* Arity-generic List. An abstraction over structured Tuple type constructor `X1[f[a]]`.
*/
trait AList:
trait AList[K[F[_]]]:
import AList.idPoly
def transform[F1[_], F2[_], Tup <: Tuple](value: Tuple.Map[Tup, F1])(
def transform[F1[_], F2[_]](value: K[F1])(
f: [a] => F1[a] => F2[a]
): Tuple.Map[Tup, F2]
): K[F2]
def traverse[F1[_], F2[_]: Applicative, Tup <: Tuple](value: Tuple.Map[Tup, F1])(
def traverse[F1[_], F2[_]: Applicative](value: K[F1])(
f: [a] => F1[a] => F2[a]
): F2[Tup]
): F2[K[Id]]
def mapN[F1[_]: Applicative, A, Tup <: Tuple](value: Tuple.Map[Tup, F1])(f: Tup => A): F1[A] =
summon[Applicative[F1]].map(traverse[F1, F1, Tup](value)(idPoly[F1]))(f)
def mapN[F1[_]: Applicative, A1](value: K[F1])(f: K[Id] => A1): F1[A1] =
summon[Applicative[F1]].map(traverse[F1, F1](value)(idPoly[F1]))(f)
def traverseX[F1[_], F2[_]: Applicative, P[_], Tup <: Tuple](value: Tuple.Map[Tup, F1])(
def traverseX[F1[_], F2[_]: Applicative, P[_]](value: K[F1])(
f: [a] => F1[a] => F2[P[a]]
): F2[Tuple.Map[Tup, P]]
): F2[K[P]]
def foldr[F1[_], A, Tup <: Tuple](value: Tuple.Map[Tup, F1], init: A)(
f: [a] => (F1[a], A) => A
): A
def foldr[F1[_], A1](value: K[F1], init: A1)(
f: [a] => (F1[a], A1) => A1
): A1
def toList[F1[_], Tup <: Tuple](value: Tuple.Map[Tup, F1]): List[F1[Any]] =
def toList[F1[_]](value: K[F1]): List[F1[Any]] =
val f = [a] => (p1: F1[a], p2: List[F1[Any]]) => p1.asInstanceOf[F1[Any]] :: p2
foldr[F1, List[F1[Any]], Tup](value, Nil)(f)
foldr[F1, List[F1[Any]]](value, Nil)(f)
end AList
object AList:
@ -49,46 +49,54 @@ object AList:
def nil[Tup <: Tuple] = EmptyTuple.asInstanceOf[Tup]
lazy val tuple: AList = new AList:
override def transform[F1[_], F2[_], Tup <: Tuple](value: Tuple.Map[Tup, F1])(
f: [x] => F1[x] => F2[x]
): Tuple.Map[Tup, F2] =
value match
case _: Tuple.Map[EmptyTuple, F1] => nil[Tuple.Map[Tup, F2]]
case (head: F1[x] @unchecked) *: tail =>
(f(head) *: transform[F1, F2, Tail[Tup]](tail.asInstanceOf)(f))
.asInstanceOf[Tuple.Map[Tup, F2]]
inline def toTuple[A](a: A): Tuple1[A] = Tuple1(a)
override def traverse[F1[_], F2[_]: Applicative, Tup <: Tuple](value: Tuple.Map[Tup, F1])(
f: [a] => F1[a] => F2[a]
): F2[Tup] =
val F2 = summon[Applicative[F2]]
value match
case _: Tuple.Map[EmptyTuple, F1] => F2.pure(nil[Tup])
case (head: F1[x] @unchecked) *: (tail: Tuple.Map[Tail[Tup], F1] @unchecked) =>
val tt = traverse[F1, F2, Tail[Tup]](tail)(f)
val g = (t: Tail[Tup]) => (h: x) => (h *: t).asInstanceOf[Tup]
F2.ap[x, Tup](F2.map(tt)(g))(f(head))
inline def fromTuple[A1, A2](f: A1 => A2): Tuple1[A1] => A2 = { case Tuple1(a) => f(a) }
override def traverseX[F1[_], F2[_]: Applicative, P[_], Tup <: Tuple](
value: Tuple.Map[Tup, F1]
)(
f: [a] => F1[a] => F2[P[a]]
): F2[Tuple.Map[Tup, P]] =
val F2 = summon[Applicative[F2]]
value match
case _: Tuple.Map[EmptyTuple, F1] => F2.pure(nil[Tuple.Map[Tup, P]])
case (head: F1[x] @unchecked) *: (tail: Tuple.Map[Tail[Tup], F1] @unchecked) =>
val tt = traverseX[F1, F2, P, Tail[Tup]](tail)(f)
val g = (t: Tuple.Map[Tail[Tup], P]) =>
(h: P[x]) => (h *: t).asInstanceOf[Tuple.Map[Tup, P]]
F2.ap[P[x], Tuple.Map[Tup, P]](F2.map(tt)(g))(f(head))
// givens for tuple map
given [Tup <: Tuple]: AList[[F[_]] =>> Tuple.Map[Tup, F]] = tuple[Tup]
override def foldr[F1[_], A, Tup <: Tuple](value: Tuple.Map[Tup, F1], init: A)(
f: [a] => (F1[a], A) => A
): A =
value match
case _: Tuple.Map[EmptyTuple, F1] => init
case (head: F1[x] @unchecked) *: tail =>
f(head, foldr[F1, A, Tail[Tup]](tail.asInstanceOf[Tuple.Map[Tail[Tup], F1]], init)(f))
def tuple[Tup <: Tuple]: AList[[F[_]] =>> Tuple.Map[Tup, F]] =
new AList[[F[_]] =>> Tuple.Map[Tup, F]]:
override def transform[F1[_], F2[_]](value: Tuple.Map[Tup, F1])(
f: [x] => F1[x] => F2[x]
): Tuple.Map[Tup, F2] =
value match
case _: Tuple.Map[EmptyTuple, F1] => nil[Tuple.Map[Tup, F2]]
case (head: F1[x] @unchecked) *: tail =>
(f(head) *: transform[F1, F2](tail.asInstanceOf)(f))
.asInstanceOf[Tuple.Map[Tup, F2]]
override def traverse[F1[_], F2[_]: Applicative](value: Tuple.Map[Tup, F1])(
f: [a] => F1[a] => F2[a]
): F2[Tuple.Map[Tup, Id]] =
val F2 = summon[Applicative[F2]]
value match
case _: Tuple.Map[EmptyTuple, F1] => F2.pure(nil[Tup].asInstanceOf[Tuple.Map[Tup, Id]])
case (head: F1[x] @unchecked) *: (tail: Tuple.Map[Tail[Tup], F1] @unchecked) =>
val tt = tuple[Tail[Tup]].traverse[F1, F2](tail)(f)
val g = (t: Tail[Tup]) => (h: x) => (h *: t)
F2.ap[x, Tup](F2.map(tt)(g.asInstanceOf))(f(head)).asInstanceOf[F2[Tuple.Map[Tup, Id]]]
override def traverseX[F1[_], F2[_]: Applicative, P[_]](
value: Tuple.Map[Tup, F1]
)(
f: [a] => F1[a] => F2[P[a]]
): F2[Tuple.Map[Tup, P]] =
val F2 = summon[Applicative[F2]]
value match
case _: Tuple.Map[EmptyTuple, F1] => F2.pure(nil[Tuple.Map[Tup, P]])
case (head: F1[x] @unchecked) *: (tail: Tuple.Map[Tail[Tup], F1] @unchecked) =>
val tt = traverseX[F1, F2, P](tail.asInstanceOf)(f)
val g = (t: Tuple.Map[Tail[Tup], P]) =>
(h: P[x]) => (h *: t).asInstanceOf[Tuple.Map[Tup, P]]
F2.ap[P[x], Tuple.Map[Tup, P]](F2.map(tt)(g.asInstanceOf))(f(head))
override def foldr[F1[_], A1](value: Tuple.Map[Tup, F1], init: A1)(
f: [a] => (F1[a], A1) => A1
): A1 =
value match
case _: Tuple.Map[EmptyTuple, F1] => init
case (head: F1[x] @unchecked) *: tail =>
f(head, foldr[F1, A1](tail.asInstanceOf, init)(f))
end AList

View File

@ -34,7 +34,7 @@ trait TypeFunctions:
final def idFun[A]: A => A = ((a: A) => a) // .setToString("TypeFunctions.id")
final def const[A, B](b: B): A => B = ((_: A) => b) // .setToString(s"TypeFunctions.const($b)")
/*
/*
final def idK[M[_]]: M ~> M = λ[M ~> M](m => m).setToString("TypeFunctions.idK")
def nestCon[M[_], N[_], G[_]](f: M ~> N): (M G)#l ~> (N G)#l =
@ -44,7 +44,8 @@ trait TypeFunctions:
type Endo[T] = T => T
type ~>|[A[_], B[_]] = A ~> Compose[Option, B]#Apply
*/
*/
type ~>|[F1[_], F2[_]] = [A] => F1[A] => Option[F2[A]]
end TypeFunctions

View File

@ -9,7 +9,7 @@ object AListTest extends BasicTestSuite:
import sbt.internal.util.AList
val tuple = t1
val f = (arg: (Int, String)) => arg._1.toString + "|" + arg._2
val actual = AList.tuple.mapN[Option, String, (Int, String)](tuple)(f)
val actual = AList.tuple[(Int, String)].mapN[Option, String](tuple)(f)
assert(actual == Option("1|foo"))
}
end AListTest