Skip to content

Instantly share code, notes, and snippets.

@heyrutvik
Last active December 3, 2020 13:52
Show Gist options
  • Save heyrutvik/ce12bc410074fea8c408e4e493d207c4 to your computer and use it in GitHub Desktop.
Save heyrutvik/ce12bc410074fea8c408e4e493d207c4 to your computer and use it in GitHub Desktop.
deriving typeclass instances using shapeless
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