Created
April 27, 2014 16:17
-
-
Save jdegoes/11349538 to your computer and use it in GitHub Desktop.
Generic encoding of data in Scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ####### 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