Skip to content

Instantly share code, notes, and snippets.

@tarao
Last active February 10, 2020 11:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tarao/80d52dff89cbbc6bdf0e07bc26b0af54 to your computer and use it in GitHub Desktop.
Save tarao/80d52dff89cbbc6bdf0e07bc26b0af54 to your computer and use it in GitHub Desktop.
Category-theoretic universal product representation
import product2.|*|
package object instance {
implicit val instanceProduct2: universal.Product2[|*|] =
new universal.Product2[|*|] {
override def p1[A, B](p: A |*| B): A = product2.π1(p)
override def p2[A, B](p: A |*| B): B = product2.π2(p)
override def mediate[A, B, C](implicit
wedge: universal.Wedge[C, A, B]
): C => A |*| B = product2.mediate(wedge.w1, wedge.w2)
}
implicit val instanceTuple2: universal.Product2[Tuple2] =
new universal.Product2[Tuple2] {
override def p1[A, B](p: (A, B)): A = p._1
override def p2[A, B](p: (A, B)): B = p._2
override def mediate[A, B, C](implicit
wedge: universal.Wedge[C, A, B]
): C => (A, B) = (c: C) => (wedge.w1(c), wedge.w2(c))
}
}
package object product2 {
sealed trait |*|[A, B] {
def apply[C](f: (A, B) => C): C
}
object |*| {
def apply[A, B](a: A, b: B): |*|[A, B] = new |*|[A, B] {
override def apply[C](f: (A, B) => C): C = f(a, b)
override def toString = s"<$a, $b>"
}
}
def π1[A, B](x: A |*| B): A = x((a: A, b: B) => a)
def π2[A, B](x: A |*| B): B = x((a: A, b: B) => b)
// mediating
def mediate[A, B, C](f: C => A, g: C => B): C => A |*| B =
(c: C) => |*|(f(c), g(c))
// example usage of mediating
def fromScala[A, B](t: (A, B)): A |*| B =
mediate[A, B, (A, B)](_._1, _._2)(t)
}
import scala.language.higherKinds
package object universal {
trait Wedge[W, A, B] {
def w1(w: W): A
def w2(w: W): B
}
trait Product2[P[_, _]] {
def p1[A, B](p: P[A, B]): A
def p2[A, B](p: P[A, B]): B
def mediate[A, B, C](implicit
wedge: Wedge[C, A, B]
): C => P[A, B]
}
def π1[A, B, P[_, _]](p: P[A, B])(implicit product: Product2[P]): A =
product.p1(p)
def π2[A, B, P[_, _]](p: P[A, B])(implicit product: Product2[P]): B =
product.p2(p)
implicit def product2IsWedge[A, B, P[_, _]](implicit
product: Product2[P]
): Wedge[P[A, B], A, B] =
new Wedge[P[A, B], A, B] {
def w1(w: P[A, B]): A = product.p1(w)
def w2(w: P[A, B]): B = product.p2(w)
}
implicit class WedgeOps[W, A, B](val w: W)(implicit
wedge: Wedge[W, A, B]
) {
def toProduct[P[_, _]](implicit
product: Product2[P],
): P[A, B] = product.mediate[A, B, W](wedge)(w)
}
}
@tarao
Copy link
Author

tarao commented Oct 2, 2019

User-defined tuple-like type

scala> import product2._
import product2._

scala> val p1 = |*|(1, "foo")
p1: Int |*| String = <1, foo>

scala> val i = π1(p1)
i: Int = 1

scala> val s = π2(p1)
s: String = foo

scala> val p2 = ("bar", 2)
p2: (String, Int) = (bar,2)

scala> fromScala(p2)
res0: String |*| Int = <bar, 2>

Use tuple-like type universally

scala> import universal._
import universal._

scala> import instance._
import instance._

scala> import product2.|*|
import product2.$bar$times$bar

scala> val p1 = |*|(1, "foo")
p1: Int |*| String = <1, foo>

scala> π1(p1)
res0: Int = 1

scala> π2(p1)
res1: String = foo

scala> val p2 = ("bar", 2)
p2: (String, Int) = (bar,2)

scala> π1(p2)
res2: String = bar

scala> π2(p2)
res3: Int = 2

scala> p1.toProduct[Tuple2]
res4: (Int, String) = (1,foo)

scala> p2.toProduct[|*|]
res5: String |*| Int = <bar, 2>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment