Skip to content

Instantly share code, notes, and snippets.

@tkrs
Last active September 22, 2016 05:50
Show Gist options
  • Save tkrs/2a63a1384871ff807ee1e8623f17000f to your computer and use it in GitHub Desktop.
Save tkrs/2a63a1384871ff807ee1e8623f17000f to your computer and use it in GitHub Desktop.
Automatic case class derivation in shapeless
λ sbt run
[info] Loading global plugins from /Users/satotakeru/.sbt/0.13/plugins
[info] Set current project to shapeless-demo (in build file:/Users/satotakeru/code/github.com/tkrs/shapeless-demo/)
[info] Running Main
Obj(List((Str(bar),Str(vvv)), (Str(hoge),Obj(List((Str(xyz),Num(1.0E8))))), (Str(quux),Obj(List((Num(10.0),Obj(List((Str(123),Num(1.0))))), (Num(20.0),Obj(List((Str(123),Num(1.0E-13))))))))))
[success] Total time: 0 s, completed Aug 25, 2016 5:04:44 PM
import shapeless._, labelled._, syntax._, ops.record._
import scala.annotation.implicitNotFound
object derive {
sealed trait KV
case class Obj(xs: List[(KV, KV)]) extends KV {
def ++(j: KV): KV = j match {
case Obj(xxs) => copy(xs ++ xxs)
case Emp => this
case Null => Null
case _ => ???
}
}
case class Arr(xs: List[KV]) extends KV
case class Str(s: String) extends KV
case class Num(n: Double) extends KV
case object Null extends KV
case object Emp extends KV
@implicitNotFound("Not found Encoder instances of the type ${A}")
trait Encoder[A] {
def apply(a: A): KV
}
final object Encoder extends LowPriorityEncoder {
def apply[A](implicit A: Encoder[A]) = A
implicit val hnilEncoder = new Encoder[HNil] {
def apply(a: HNil) = Emp
}
implicit def recordEncoder[K0, V0, T <: HList]
(implicit wk: Witness.Aux[K0], tv: Typeable[V0],
k: Lazy[Encoder[K0]], v: Lazy[Encoder[Option[V0]]], t: Lazy[Encoder[T]]) =
new Encoder[FieldType[K0, V0] :: T] {
def apply(h: FieldType[K0, V0] :: T): KV =
Obj(List(k.value(wk.value) -> v.value(tv.cast(h.head)))) ++ t.value(h.tail)
}
implicit def ccEncoder[A, H <: HList]
(implicit gen: LabelledGeneric.Aux[A, H], repr: Lazy[Encoder[H]]) =
new Encoder[A] {
def apply(a: A): KV = repr.value(gen.to(a))
}
}
trait LowPriorityEncoder {
implicit def symbolEncoder[S <: Symbol]: Encoder[S] = new Encoder[S] {
def apply(a: S) = Str(a.name)
}
implicit val stringEncoder: Encoder[String] = new Encoder[String] {
def apply(a: String) = Str(a)
}
implicit val doubleEncoder: Encoder[Double] = new Encoder[Double] {
def apply(a: Double) = Num(a)
}
implicit val intEncoder: Encoder[Int] = new Encoder[Int] {
def apply(a: Int) = Num(a.toDouble)
}
implicit val longEncoder: Encoder[Long] = new Encoder[Long] {
def apply(a: Long) = Num(a.toDouble)
}
implicit def optionEncoder[A](implicit A: Encoder[A]): Encoder[Option[A]] =
new Encoder[Option[A]] {
def apply(a: Option[A]) = a.fold[KV](Null)(A(_))
}
implicit def mapEncoder[M[K, +V] <: Map[K, V], K, V]
(implicit K: Encoder[K], V: Encoder[V]): Encoder[M[K, V]] =
new Encoder[M[K, V]] {
def apply(a: M[K, V]) = Obj(
a.foldRight[List[(KV, KV)]](Nil) {
case ((k, v), acc) => (K(k), V(v)) :: acc
}
)
}
// more instances ...
}
}
object Main extends App {
import derive._
case class H(xyz: Long)
case class G(`123`: Double)
case class F(bar: String, hoge: H, quux: Map[Int, G])
def f[A](a: A)(implicit A: Encoder[A]): KV = A(a)
println(f(F("vvv", H(100000000L), Map(10 -> G(1.0), 20 -> G(0.0000000000001)))))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment