Skip to content

Instantly share code, notes, and snippets.

@jdegoes
Created April 27, 2014 16:17
Show Gist options
  • Save jdegoes/11349538 to your computer and use it in GitHub Desktop.
Save jdegoes/11349538 to your computer and use it in GitHub Desktop.
Generic encoding of data in Scala
// ####### If you use a more generic lens you can transform types along the way.
case class Lens[S, A](get: S => A, set: (S, A) => S)
case class Prism[S, A](get: S => Option[A], unget: A => S) extends (A => S) {
def apply(a: A): S = unget(a)
def unapply(s: S): Option[A] = get(s)
}
// ####### Sum type
sealed trait |+| [A, B]
final case class Left[A, B](value: A) extends (A |+| B)
final case class Right[A, B](value: B) extends (A |+| B)
// ####### Product type
final case class |*| [A, B](left: A, right: B)
def P[A, B] = |*|.apply[A, B] _ // Scala recursion workaround
implicit class PSyntax[A](a: A) {
def |*| [B](b: B): A |*| B = P(a, b)
}
// ####### Imagine arities of up to 19 or something
trait P2[A, B] {
type Type = A |*| B
def apply(a: A, b: B) = a |*| b
def unapply(v: A |*| B): Option[(A, B)] = Some((v.left, v.right))
def _1 = Lens[A |*| B, A](get = _.left, set = (s, a) => s.copy(left = a))
def _2 = Lens[A |*| B, B](get = _.right, set = (s, a) => s.copy(right = a))
}
trait S2[A, B] {
type Type = A |+| B
def fold[Z](v: A |+| B)(left: A => Z, right: B => Z): Z = v match {
case Left(v) => left(v)
case Right(v) => right(v)
}
def _1 = Prism[A |+| B, A](get = _ match { case Left(v) => Some(v); case _ => None }, unget = Left(_))
def _2 = Prism[A |+| B, B](get = _ match { case Right(v) => Some(v); case _ => None }, unget = Right(_))
}
// ####### Construction of Products
case class Name(value: String)
case class Age(value: Int)
object Person extends P2[Name, Age] { // Compare to: case class Person(name: Name, age: Age)
def name = _1
def age = _2
}
val p: Person.Type = Person(Name("John Doe"), Age(23))
p match {
case Person(Name(n), Age(a)) => n + ": " + a
}
// ####### Construction of Sums (of Products)
object Rect extends P2[Int, Int]
object Circle extends P2[Int, Int]
object Shape extends S2[Rect.Type, Circle.Type] {
def Rect = _1
def Circle = _2
}
val s: Shape.Type = Shape.Rect(Rect(1, 2))
s match {
case Shape.Rect(rect) => rect : Rect.Type
case Shape.Circle(circle) => circle : Circle.Type
}
Shape.fold(s)(rect => "Rect!", circle => "Circle!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment