Last active
December 3, 2020 13:52
-
-
Save heyrutvik/ce12bc410074fea8c408e4e493d207c4 to your computer and use it in GitHub Desktop.
deriving typeclass instances using 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
package example | |
import shapeless._ | |
object TypeclassDerivation extends App { | |
println { | |
CsvEncoder.writeCsv(IceCream("Sunday", 25)) | |
// "Sunday,25" | |
} | |
println { | |
CsvEncoder.writeCsv(Employee("A", 52, manager = true)) | |
// "A,52,true" | |
} | |
println { | |
List[Shape]( | |
Rectangle(3.0, 4.0), | |
Circle(1.0) | |
).map(CsvEncoder.writeCsv(_)).mkString("\n") | |
// "3.0,4.0" | |
// "1.0" | |
} | |
} | |
case class Employee(name: String, age: Int, manager: Boolean) | |
case class IceCream(name: String, price: Int) | |
sealed trait Shape | |
final case class Rectangle(width: Double, height: Double) extends Shape | |
final case class Circle(radius: Double) extends Shape | |
trait CsvEncoder[A] { | |
def encode(value: A): List[String] | |
} | |
object CsvEncoder { | |
def writeCsv[A](value: A)(implicit encoder: CsvEncoder[A]): String = | |
encoder.encode(value).mkString(",") | |
def apply[A](implicit encode: CsvEncoder[A]): CsvEncoder[A] = encode | |
def instance[A](func: A => List[String]): CsvEncoder[A] = new CsvEncoder[A] { | |
def encode(value: A): List[String] = | |
func(value) | |
} | |
implicit val stringEncoder: CsvEncoder[String] = instance(string => List(string)) | |
implicit val intEncoder: CsvEncoder[Int] = instance(int => List(int.toString)) | |
implicit val booleanEncoder: CsvEncoder[Boolean] = instance(bool => List(bool.toString)) | |
implicit val doubleEncoder: CsvEncoder[Double] = instance(double => List(double.toString)) | |
implicit val hnilEncoder: CsvEncoder[HNil] = instance(_ => Nil) | |
implicit def hlistEncoder[H, T <: HList](implicit | |
hEncoder: CsvEncoder[H], | |
tEncoder: CsvEncoder[T] | |
): CsvEncoder[H :: T] = instance { case h :: t => | |
hEncoder.encode(h) ++ tEncoder.encode(t) | |
} | |
implicit val cnilEncoder: CsvEncoder[CNil] = instance(_ => Nil) | |
implicit def coproductEncoder[H, T <: Coproduct](implicit | |
hEncoder: CsvEncoder[H], | |
tEncoder: CsvEncoder[T] | |
): CsvEncoder[H :+: T] = | |
instance { | |
case Inl(h) => hEncoder.encode(h) | |
case Inr(t) => tEncoder.encode(t) | |
} | |
implicit def genericEncoder[A, R](implicit gen: Generic.Aux[A, R], enc: CsvEncoder[R]): CsvEncoder[A] = | |
instance(v => enc.encode(gen.to(v))) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment