Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Shapeless Typeclass Example
import shapeless._
import scala.{:: => :::}
import cats.implicits._
trait CsvDecoder[A] {
def decode(value: List[String]): Option[A]
}
val columnSplitter = """,(?=([^\"]*\"[^\"]*\")*[^\"]*$)""".r
def decodeCsv[A](input: String)(implicit decoder: CsvDecoder[A]): List[A] = {
input.linesIterator.map { line =>
val columns = columnSplitter.split(line).toList
decoder.decode(columns)
}.collect { case Some(x) => x }
.toList
}
// decoders for primitive types
implicit val stringDecoder: CsvDecoder[String] = strings => strings.headOption
implicit val intDecoder: CsvDecoder[Int] = strings => strings.headOption.map(_.toInt)
implicit val booleanDecoder: CsvDecoder[Boolean] = strings => strings.headOption.map(_.toBoolean)
// decoder for HNil
implicit val hNilDecoder: CsvDecoder[HNil] = _ => Some(HNil)
// decoder for recursive HList
implicit def hlistDecoder[H, T <: HList](implicit
hDecoder: CsvDecoder[H],
tDecoder: CsvDecoder[T]
): CsvDecoder[H :: T] = {
case Nil => None
case h ::: t => (hDecoder.decode(List(h)), tDecoder.decode(t)).mapN((h1, t1) => h1 :: t1)
}
final case class User(name: String, age: Int, admin: Boolean)
// user decoder
//implicit def userDecoder: CsvDecoder[User] = {
// val gen = Generic[User]
// val decoder = implicitly[CsvDecoder[String :: Int :: HNil]]
// strings => {
// val maybeGeneric: Option[gen.Repr] = decoder.decode(strings)
// maybeGeneric.map(r => gen.from(r))
// }
//}
// from generic decoder to case class
implicit def caseClassDecoder[A, R](implicit gen: Generic.Aux[A, R],
decoder: CsvDecoder[R]): CsvDecoder[A] =
strings => decoder.decode(strings).map(r => gen.from(r))
val decoded = decodeCsv[User]("Alice,32,true\nBob,35,false\nCharlie,35,false")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment