Skip to content

Instantly share code, notes, and snippets.

@carymrobbins
Created August 9, 2017 20:45
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 carymrobbins/c953345ed5d5c7d0dd952018b1587128 to your computer and use it in GitHub Desktop.
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.
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