Last active
September 22, 2016 05:50
-
-
Save tkrs/2a63a1384871ff807ee1e8623f17000f to your computer and use it in GitHub Desktop.
Automatic case class derivation in shapeless
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
λ 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 |
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
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