Created
June 9, 2020 04:46
-
-
Save szoio/3380608c26b35b79914bc14605a5d2e4 to your computer and use it in GitHub Desktop.
Shapeless Typeclass Example
This file contains hidden or 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._ | |
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