Created
August 9, 2017 20:45
-
-
Save carymrobbins/c953345ed5d5c7d0dd952018b1587128 to your computer and use it in GitHub Desktop.
A minimal Shapeless-like implementation and demonstration of generic programming in a single file.
This file contains 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
sealed trait HList | |
type HNil = HNil.type | |
case object HNil extends HList | |
case class ::[H, T <: HList](head: H, tail: T) extends HList | |
trait Generic[A] { | |
type Repr | |
def to(a: A): Repr | |
def from(r: Repr): A | |
} | |
object Generic { | |
type Aux[A, R] = Generic[A] { type Repr = R } | |
def arity3[CC, A, B, C]( | |
ap: (A, B, C) => CC, | |
un: CC => Option[(A, B, C)] | |
): Aux[CC, A :: B :: C :: HNil] = new Generic[CC] { | |
override type Repr = A :: B :: C :: HNil | |
override def from(r: Repr): CC = r match { case a :: b :: c :: HNil => | |
ap(a, b, c) | |
} | |
override def to(cc: CC): Repr = { | |
val (a, b, c) = un(cc).getOrElse(throw new RuntimeException("Could not unapply type")) | |
::(a, ::(b, ::(c, HNil))) | |
} | |
} | |
} | |
trait Csv[A] { | |
def apply(a: A): List[String] | |
} | |
object Csv { | |
def apply[A](implicit ev: Csv[A]): Csv[A] = ev | |
def instance[A](f: A => List[String]): Csv[A] = new Csv[A] { | |
override def apply(a: A): List[String] = f(a) | |
} | |
def encode[A](a: A)(implicit csv: Csv[A]): List[String] = csv(a) | |
implicit def auto[A, L <: HList]( | |
implicit | |
g: Generic.Aux[A, L], | |
lCsv: Csv[L] | |
): Csv[A] = instance(a => lCsv(g.to(a))) | |
implicit val hnil: Csv[HNil] = instance(_ => Nil) | |
implicit def hlist[H, T <: HList]( | |
implicit | |
hCsv: Field[H], | |
tCsv: Csv[T] | |
): Csv[H :: T] = instance { case h :: t => hCsv(h) +: tCsv(t) } | |
trait Field[A] { | |
def apply(a: A): String | |
} | |
object Field { | |
def apply[A](implicit ev: Field[A]): Field[A] = ev | |
def instance[A](f: A => String): Field[A] = new Field[A] { | |
override def apply(a: A): String = f(a) | |
} | |
def fromToString[A]: Field[A] = _fromToString.asInstanceOf[Field[A]] | |
private val _fromToString = instance[Any](_.toString) | |
implicit val string: Field[String] = instance(identity) | |
implicit val int: Field[Int] = fromToString | |
implicit val double: Field[Double] = fromToString | |
} | |
} | |
case class Foo(a: String, b: Int, c: Double) | |
implicit val genericFoo = Generic.arity3(Foo.apply, Foo.unapply) | |
Csv.encode(Foo("bar", 12, 23.43)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment