circeから学ぶ GenericProgramming入門 - Scala関西Summit 2018

circeから学ぶ GenericProgramming入門 - Scala関西Summit 2018

1b963e66060bcc77b7ed647f5b6ff3a7?s=128

Shinichi Morimoto

November 10, 2018
Tweet

Transcript

  1. 2.

    ࣗݾ঺հ • ৿ຊ ਅҰ • @shnmorimoto • Fringe81 גࣜձࣾ •

    αʔόαΠυΤϯδχΞ • Scalaྺ1೥൒ • ؔ੢ग़਎ 
  2. 4.

    circe • JSON library • ݺͼํ • αʔγʔʁΩϧέʔʁ • EncoderͷAutomatic

    Derivationʢࣗಈಋग़ʣ • Φϓγϣϯ͕๛෋Ͱᙱ͍ͱ͜Ζ·Ͱख͕ಧ͘ 
  3. 5.

    circe • JSON library • ݺͼํ • αʔγʔʁΩϧέʔʁ • EncoderͷAutomatic

    Derivationʢࣗಈಋग़ʣ • Φϓγϣϯ͕๛෋Ͱᙱ͍ͱ͜Ζ·Ͱख͕ಧ͘ 
  4. 6.

    Automatic Derivationʢࣗಈಋग़ʣ  import io.circe.generic.auto._, io.circe.syntax._ case class Person(name: String)

    case class Greeting(salutation: String, person: Person, exclamationMarks: Int) val greeting = Greeting("Hey", Person("Chris"), 3).asJson println(greeting) \ TBMVUBUJPO)FZ  QFSTPO\ OBNF$ISJT ^  FYDMBNBUJPO.BSLT ^ ࣮ߦ݁Ռ CVJMETCUͷهࡌ͸লུ
  5. 7.

    Automatic Derivationʢࣗಈಋग़ʣ import io.circe.generic.auto._, io.circe.syntax._ case class Person(name: String) case

    class Greeting(salutation: String, person: Person, exclamationMarks: Int) val greeting = Greeting("Hey", Person("Chris"), 3).asJson println(greeting) ᶃ ᶄ ᶅ 1. circeΛimport 2. JSONʹม׵͍ͨ͠case classΛఆٛ 3. JSONʹม׵ 
  6. 10.

    ࠓ೔࿩͢͜ͱ/࿩͞ͳ͍͜ͱ • ࿩͢͜ͱ • Generic Programmingʹ͍ͭͯ • shapelessͷجຊతͳ࢖͍ํ • ࿩͞ͳ͍͜ͱ

    • circeͷৄࡉͳ࢖͍ํ • shapelessͷ಺෦࣮૷ • ଟݴޠʹ͓͚ΔGeneric Programmingͷྫ 
  7. 12.

    ຊ୊ʹೖΔલʹ… import io.circe.generic.auto._, io.circe.syntax._ case class Person(name: String) case class

    Greeting(salutation: String, person: Person, exclamationMarks: Int) val greeting = Greeting("Hey", Person("Chris"), 3).asJson println(greeting) asJsonϝιου͸ - case classʹఆٛ͞Ε͍ͯͳ͍ - case classʹ͸਌class͕͍ͳ͍ʢܧঝ͍ͯ͠ͳ͍ʣ ͷʹͳͥ࢖͑ΔͷͩΖ͏ʁ 
  8. 13.

    circeͷ࣮ࡍͷίʔυ  package object syntax { implicit final class EncoderOps[A](val

    wrappedEncodeable: A) extends AnyVal { final def asJson(implicit encoder: Encoder[A]): Json = encoder(wrappedEncodeable) final def asJsonObject(implicit encoder: ObjectEncoder[A]): JsonObject = encoder.encodeObject(wrappedEncodeable) } implicit final class StringOps(val value: String) extends AnyVal { final def :=[A: Encoder](a: A): (String, Json) = (value, a.asJson) } } w FOSJDINZMJCSBSZύλʔϯ w ܕΫϥε
  9. 14.

    enrich my libraryύλʔϯ  JNQMJDJUDMBTTΛఆٛ͢Δ͜ͱʹΑΓɺطଘͷܕʹϝ ιουΛ௥Ճ͢Δ͜ͱͰ͖Δ implicit class RichString(val value:

    String) extends AnyVal { def exclamation: String = value + "!" } println("Hello".exclamation) ࣮ߦ݁Ռ )FMMP
  10. 15.

    ܕΫϥε  1FSTPO %PH *OU 1PJOU 4IPX &R %FCVH +TPO

    $TW ༷ʑͳܕ ༷ʑͳৼΔ෣͍ ܕʹରͯ͠ڞ௨ͷৼΔ෣͍ Λ௥Ճ͍ͨ͠
  11. 16.

    ܕΫϥεʢCSVग़ྗʣ  ࣮ߦ݁Ռ case class Person(name: String, age: Int, isManager:

    Boolean) case class Dog(name: String, age: Int, woolly: Boolean) val person = Person("person", 29, true) val dog = Dog("dog", 10, false) println(asCsv(person)) println(asCsv(dog)) QFSTPO  ZFT EPH  OP ར༻ྫ
  12. 17.

    ܕΫϥεͷར༻ʢCSVग़ྗʣ  def asCsv(value: Any): String = { value.???? }

    BT$TWؔ਺ͷఆٛ • ༷ʑͳܕʹద༻Ͱ͖ΔΑ͏ʹAnyܕΛड͚औΔʁ • AnyܕͳͷͰɺϝιου͕ඇৗʹݶΒΕΔ…ɻ
  13. 19.

    ܕΫϥεͷར༻ʢCSVग़ྗʣ BT$TWؔ਺ͷఆٛ  def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ= enc.encode(value).mkString(",")

    • ར༻͍ͨ͠ܕʹ͍ͭͯimplicitͰͦͷܕΫϥεͷৼΔ෣͍Λ࣮૷͠ ͨॲཧΛड͚औΔ • ܕΫϥεͷΠϯελϯεΛҾ਺Ͱड͚औΔ
  14. 23.

    ܕΫϥεͷΠϯελϯε ܕΫϥεͷΠϯελϯεΛ࡞੒ case class Person(name: String, age: Int, isManager: Boolean)

    case class Dog(name: String, age: Int, woolly: Boolean) ܕΫϥεͷΠϯελϯεͱ͍ͨ͠ܕ [A]ʹΠϯελϯεͱ͍ͨ͠ܕΛ౰ͯ͸ΊͯɺencodeΛ࣮૷͢Δ  implicit val personEncoder = new CsvEncoder[Person] { override def encode(value: Person): List[String] = List( value.name, value.age.toString, if (value.isManager) "yes" else "no" ) } implicit val dogEncoder = new CsvEncoder[Dog] { override def encode(value: Dog): List[String] = List( value.name, value.age.toString, if (value.woolly) "yes" else "no" ) }
  15. 24.

    ܕΫϥεͷར༻ $47ग़ྗ ܕΫϥεΛར༻͢Δؔ਺ͷఆٛ val person = Person("person", 29, true) val

    dog = Dog("dog", 10, false) println(asCsv(person)) println(asCsv(dog)) • AͷܕͱܕΫϥεͷΠϯελϯεΛҾ਺ʹऔΔؔ਺Λఆٛ • ར༻͢Δଆ͸implicitͰܕΫϥεͷΠϯελϯεΛ౉͢  def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ= enc.encode(value).mkString(",")
  16. 25.

    ܕΫϥεͷར༻ $47ग़ྗ JNQMJDJUDMBTTΛఆٛ val person = Person("person", 29, true) val

    dog = Dog("dog", 10, false) println(person.asCsv) println(dog.asCsv) implicit classΛఆٛ͢Δ͜ͱʹΑΓɺasCsvͷΑ͏ͳܗͰར༻Ͱ͖Δ  implicit class CsvEncoderOps[A](val value: A) extends AnyVal { def asCsv(implicit enc: CsvEncoder[A]): String = enc.encode(value).mkString(",") } // Or implicit class CsvEncoderOps[A](val value: A) extends AnyVal { def asCsv: String = implicitly[CsvEncoder[A]].encode(value).mkString(",") }
  17. 26.

    ܕΫϥε αϯϓϧίʔυ  trait CsvEncoder[A] { def encode(value: A): List[String]

    } object CsvEncoder { implicit val personEncoder = new CsvEncoder[Person] { override def encode(value: Person): List[String] = List( value.name, value.age.toString, if (value.isManager) "yes" else "no" ) } implicit val dogEncoder = new CsvEncoder[Dog] { override def encode(value: Dog): List[String] = List( value.name, value.age.toString, if (value.woolly) "yes" else "no" ) } implicit class CsvEncoderOps[A](value: A) { def asCsv(implicit enc: CsvEncoder[A]): String = enc.encode(value).mkString(",") } // Or //implicit class CsvEncoderOps[A](value: A) { // def asCsv: String = implicitly[CsvEncoder[A]].encode(value).mkString(",") //} } object Main extends App { import CsvEncoder._ case class Person(name: String, age: Int, isManager: Boolean) case class Dog(name: String, age: Int, woolly: Boolean) val person = Person("person", 29, true) val dog = Dog("dog", 10, false) println(person.asCsv) println(dog.asCsv) }
  18. 27.

    ܕΫϥεΠϝʔδ  USBJU$TW&ODPEFS<"> 1FSTPO %PH $BU USBJU+TPO&ODPEFS<"> USBJU&R<"> $TW&ODPEFS<1FSTPO> $TW&ODPEFS<%PH>

    $TW&ODPEFS<$BU> +TPO&ODPEFS<1FSTPO> +TPO&ODPEFS<%PH> BT$TW BT+TPO FR ۩ମతͳܕ ֦ு͍ͨ͠ػೳ ܕΫϥε ֤ܕͷ ܕΫϥεΠϯελϯε ܕΫϥεΠϯελϯε ΛҾ਺ͱ͢Δؔ਺ DMBTT DBTFDMBTT USBJU JOTUBODF ؔ਺
  19. 28.

    ܧঝΛ࢖͏৔߹  ܧঝΛར༻ͯ͠ɺಉ༷ͷࣄΛ࣮ݱ trait CsvEncoder { def asCSV: String }

    object Main extends App { case class Person(name: String, age: Int, isManager: Boolean) extends CsvEncoder { override def asCSV: String = List(name, age.toString, if (isManager) "yes" else "no").mkString(",") } case class Dog(name: String, age: Int, woolly: Boolean) extends CsvEncoder { override def asCSV: String = List(name, age.toString, if (woolly) "yes" else "no").mkString(",") } val person = Person("person", 29, true) val dog = Dog("dog", 10, false) println(person.asCSV) println(dog.asCSV) }
  20. 31.

    JSON ࠶ܝ import io.circe.generic.auto._, io.circe.syntax._ case class Person(name: String) case

    class Greeting(salutation: String, person: Person, exclamationMarks: Int) val greeting = Greeting("Hey", Person("Chris"), 3).asJson println(greeting) asJson͸ܕΫϥεʹΑ࣮ͬͯݱ͞Ε͍ͯΔ͜ͱ͕෼͔ͬͨ 
  21. 32.

    circeͷ࣮ࡍͷίʔυ  package object syntax { implicit final class EncoderOps[A](val

    wrappedEncodeable: A) extends AnyVal { final def asJson(implicit encoder: Encoder[A]): Json = encoder(wrappedEncodeable) final def asJsonObject(implicit encoder: ObjectEncoder[A]): JsonObject = encoder.encodeObject(wrappedEncodeable) } implicit final class StringOps(val value: String) extends AnyVal { final def :=[A: Encoder](a: A): (String, Json) = (value, a.asJson) } } w &ODPEFSͷܕΫϥεΛར༻͢Δʹ͸Πϯελϯε͕ඞཁ w Πϯελϯε͸Ͳ͜ʹ΋ఆٛ͞Ε͍ͯͳ͍ w Ͱ΋BT+TPO͸ར༻Ͱ͖͍ͯΔ ෆࢥٞʁ
  22. 34.

    shapeless TIBQFMFTTJTBUZQFDMBTTBOEEFQFOEFOUUZQFCBTFEHFOFSJDQSPHSBNNJOH MJCSBSZGPS4DBMB ग़యIUUQTHJUIVCDPNNJMFTTBCJOTIBQFMFTT • Generic ProgrammingΛ࣮ݱ͢Δ Scala Library •

    2011೥͔ΒMiles Sabin (@milessabin)͞Μ͕։ൃ։࢝ • HaskellͷScrap Your Boilerplate(SYB)Λ࣮ݱ͍ͯ͠Δ • circe, spary-json-shapeless, specs2, etc…Ͱར༻͞Ε͍ͯΔ 
  23. 35.

    circeͷ࣮ࡍͷίʔυ ࠶ܝ  package object syntax { implicit final class

    EncoderOps[A](val wrappedEncodeable: A) extends AnyVal { final def asJson(implicit encoder: Encoder[A]): Json = encoder(wrappedEncodeable) final def asJsonObject(implicit encoder: ObjectEncoder[A]): JsonObject = encoder.encodeObject(wrappedEncodeable) } implicit final class StringOps(val value: String) extends AnyVal { final def :=[A: Encoder](a: A): (String, Json) = (value, a.asJson) } } DJSDFͰ͸&ODPEFSΠϯελϯεͷࣗಈతͳಋग़Λ TIBQFMFTTΛར༻࣮ͯ͠ݱ͍ͯ͠Δ
  24. 42.

    case classͱtuple case class Person(name: String, age: Int, hat: Boolean)

    case class Food(name: String, size: Int, hot: Boolean) val person = Person("person", 29, true) val ramen = Food("ramen", 3, true) val tuple: (String, Int, Boolean) = ("tuple", 25, false) • Person͸Stringܕ, Intܕ, BooleanܕͷϑΟʔϧυΛ͍࣋ͬͯΔ • Food͸Stringܕ, Intܕ, BooleanܕͷϑΟʔϧυΛ͍࣋ͬͯΔ • ্هྫͷtuple͸Stringܕ, Intܕ, BooleanܕΛ͍࣋ͬͯΔ  DBTFDMBTTͱUVQMF͸99ܕͷ஋Λ࣋ͭ σʔλߏ଄ͱͯ͠ҰൠԽ͢Δ͜ͱ͕Ͱ͖Δ
  25. 45.

    HList (Heterogeneous List) ܕΛอͬͨ··ɺҟͳΔܕͷཁૉΛؚΊΔ ͜ͱ͕Ͱ͖ΔList  import shapeless.{::, HNil} val

    personRep: String :: Int :: Boolean :: HNil = "person" :: 29 :: true :: HNil 4USJOH *OU #PPMFBO )/JM HList
  26. 46.

    HList (Heterogeneous List) val hlist = 1 :: "hoge" ::

    true :: 23 :: HNil hlist.head // 1 hlist.tail // "hoge" :: true :: 23 :: HNil hlist.tail.head // “hoge" val newHlist = 42 :: hlist // 42 :: 1 :: "hoge" :: true :: 23 :: HNil • Listͱಉ༷ʹhead, tail͕ར༻Ͱ͖Δ • ཁૉΛ௥Ճͯ͠৽͍͠HListΛ࡞ΕΔ  )-JTUͷૢ࡞
  27. 47.

    HList (Heterogeneous List) val person = Person("person", 29, true) val

    personGen = Generic[Person] val repr = personGen.to(person) val person2 = personGen.from(repr) println(repr) println(person2) ᶃ ᶄ ᶅ QFSTPOUSVF)/JM 1FSTPO QFSTPO  USVF ࣮ߦ݁Ռ 1. PersonͷGenericͳදݱΛఆٛ 2. 1Λར༻ͯ͠ɺcase classΛHListʹม׵ 3. HList͔Βcase classʹม׵ 
  28. 50.

    HListͷܕΫϥεΠϯελϯε 1. CsvEncoderܕΫϥεఆٛ 2. ૊ΈࠐΈܕͷΠϯελϯεΛ४උ  trait CsvEncoder[A] { def

    encode(value: A): List[String] } object CsvEncoder { implicit val stringEncoder: CsvEncoder[String] = new CsvEncoder[String] { override def encode(value: String): List[String] = List(value) } implicit val intEncoder: CsvEncoder[Int] = new CsvEncoder[Int] { override def encode(value: Int): List[String] = List(value.toString) } implicit val booleanEncoder: CsvEncoder[Boolean] = new CsvEncoder[Boolean] { override def encode(value: Boolean): List[String] = List( if (value) "yes" else "else" ) } } ᶃ ᶄ
  29. 51.

    HListͷܕΫϥεΠϯελϯε 1. HListશମͷΠϯελϯε 2. HNilͷΈͷΠϯελϯε  implicit def hlistEncoder[H, T

    <: HList]( implicit hEncoder: CsvEncoder[H], tEncoder: CsvEncoder[T] ): CsvEncoder[H :: T] = new CsvEncoder[H :: T] { override def encode(value: H :: T): List[String] = { value match { case h :: t => hEncoder.encode(h) ++ tEncoder.encode(t) } } } implicit val hnilEncoder: CsvEncoder[HNil] = new CsvEncoder[HNil] { override def encode(value: HNil): List[String] = Nil } ᶃ ᶄ
  30. 52.

    implicit def hlistEncoder[H, T <: HList]( implicit hEncoder: CsvEncoder[H], tEncoder:

    CsvEncoder[T] ): CsvEncoder[H :: T] = new CsvEncoder[H :: T] { override def encode(value: H :: T): List[String] = { value match { case h :: t => hEncoder.encode(h) ++ tEncoder.encode(t) } } } implicit val hnilEncoder: CsvEncoder[HNil] = new CsvEncoder[HNil] { override def encode(value: HNil): List[String] = Nil } HListͷܕΫϥεΠϯελϯε 1. Head(ઌ಄)ཁૉΛΤϯίʔυ͢Δ 2. ࠶ؼߏ଄ʹͳ͍ͬͯͯɺ • Tail͕HListͳΒɺ࠶ؼͰ͞ΒʹhlistEncoderΛධՁ • HNilʹୡͨ͠ΒɺhnilEncoderΛධՁ  ᶃ ᶄ
  31. 53.

    HListͷܕΫϥεΠϯελϯε case class Person(name: String, age: Int, hat: Boolean) val

    person = Person("person", 29, true) val personGen = Generic[Person] val personRep = personGen.to(person) // “person” :: 29 :: true :: HNil println(personRep.asCsv) ࣮ߦ݁Ռ QFSTPO  ZFT HListͷܕΫϥεΠϯελϯεΛ࢖ͬͯɺcsvग़ྗ 
  32. 56.

    HListͷܕΫϥεΠϯελϯε  TUSJOH&ODPEFS I&ODPEFS lQFSTPOz U&ODPEFS USVF)/JM IMJTU&ODPEFS JOU&ODPEFS I&ODPEFS

     U&ODPEFS USVF)/JM IMJTU&ODPEFS -JTU lQFSTPOz -JTU lQFSTPOz -JTU lz CPPMFBO&ODPEFS I&ODPEFS USVF IOJM&ODPEFS U&ODPEFS USVF ᶃ ᶄ ᶅ
  33. 57.

    HListͷܕΫϥεΠϯελϯε  TUSJOH&ODPEFS I&ODPEFS lQFSTPOz U&ODPEFS USVF)/JM IMJTU&ODPEFS JOU&ODPEFS I&ODPEFS

     U&ODPEFS USVF)/JM IMJTU&ODPEFS -JTU lQFSTPOz -JTU lQFSTPOz -JTU lz CPPMFBO&ODPEFS I&ODPEFS USVF IOJM&ODPEFS U&ODPEFS )/JM -JTU lQFSTPOz -JTU lz -JTU lZFTz /JM ᶃ ᶄ ᶅ ᶆ
  34. 60.

    PersonͷܕΫϥεΠϯελϯε ࣮ߦ݁Ռ QFSTPO  ZFT implicit val personEncoder: CsvEncoder[Person] =

    { val gen = Generic[Person] val enc: CsvEncoder[gen.Repr] = implicitly new CsvEncoder[Person] { override def encode(value: Person): List[String] = enc.encode(gen.to(value)) } } val person = Person("person", 29, true) println(person.asCsv) ᶃ ᶄ 1. gen.ReprͰPersonͷHListΛऔಘͰ͖ΔʢString::Int::Boolean::HNilʣ 2. PersonΛHListʹม׵
  35. 61.

    GenericͳܕΫϥεΠϯελϯε  1FSTPOܕݻఆ (FOFSJDͳܕ implicit val personEncoder: CsvEncoder[Person] = {

    val gen = Generic[Person] val enc: CsvEncoder[gen.Repr] = implicitly new CsvEncoder[Person] { override def encode(value: Person): List[String] = enc.encode(gen.to(value)) } } implicit def genericEncoder[A]( implicit gen: Generic[A], enc: CsvEncoder[gen.Repr]): CsvEncoder[A] = new CsvEncoder[A] { override def encode(value: A): List[String] = enc.encode((gen.to(value))) } [A]ͷܕΫϥεΠϯελϯεʹม׵
  36. 62.

    implicit def genericEncoder[A]( implicit gen: Generic[A], enc: CsvEncoder[gen.Repr]): CsvEncoder[A] =

    new CsvEncoder[A] { override def encode(value: A): List[String] = enc.encode((gen.to(value))) } GenericͳܕΫϥεΠϯελϯε <FSSPS>6TFSTNPSJNPUP8PSLDJSDF@TBNQMFTSDNBJOTDBMBFYBNQMF)FMMPTDBMBJMMFHBM EFQFOEFOUNFUIPEUZQFQBSBNFUFSNBZPOMZCFSFGFSFODFEJOBTVCTFRVFOUQBSBNFUFSTFDUJPO <FSSPS>HFO(FOFSJD<">  <FSSPS>? <FSSPS>POFFSSPSGPVOE <FSSPS> $PNQJMFDPNQJMF*ODSFNFOUBM $PNQJMBUJPOGBJMFE ɹίϯύΠϧΤϥʔɹ • Ҿ਺͸gen, enc • encͷܕͱͯ͠ɺgen.ReprΛར༻͍ͯ͠Δ͕ɺScalaͷจ๏্ɺ͜Ε͸ڐ͞Ε͍ͯͳ͍ 
  37. 63.

    implicit def genericEncoder[A, R]( implicit gen: Generic[A] { type Repr

    = R }, enc: CsvEncoder[R] ): CsvEncoder[A] = new CsvEncoder[A] { override def encode(value: A): List[String] = enc.encode(gen.to(value)) } GenericͳܕΫϥεΠϯελϯε • ܕҾ਺ͱͯ͠RΛ௥Ճ • genͷதͰ Type AliasͰReprͱRΛඥ͚ͮ • encͷܕҾ਺ͱͯ͠ɺRΛࢦఆ 
  38. 64.

    GenericͳܕΫϥεΠϯελϯε object Generic { type Aux[T, Repr0] = Generic[T] {

    type Repr = Repr0 } } shapelessͰAuxͱ͍͏type alias͕ఆٛ͞Ε͍ͯΔ  AuxͰॻ͖׵͑ implicit def genericEncoder[A, R]( implicit gen: Generic.Aux[A, R], enc: CsvEncoder[R] ): CsvEncoder[A] = new CsvEncoder[A] { override def encode(value: A): List[String] = enc.encode(gen.to(value)) }
  39. 65.

    GenericͳܕΫϥεΠϯελϯε ࣮ߦ݁Ռ QFSTPO  ZFT ࣗ෼Ͱఆٛͨ͠case classʹ͍ͭͯ ࣗಈతʹܕΫϥεͷΠϯελϯεΛಋग़Ͱ͖ͨʂ  implicit

    def genericEncoder[A, R]( implicit gen: Generic.Aux[A, R], enc: CsvEncoder[R] ): CsvEncoder[A] = new CsvEncoder[A] { override def encode(value: A): List[String] = enc.encode(gen.to(value)) } val person = Person("person", 29, true) println(person.asCsv)
  40. 66.

    GenericͳܕΫϥεɹαϯϓϧίʔυ  trait CsvEncoder[A] { def encode(value: A): List[String] }

    object CsvEncoder { implicit val stringEncoder: CsvEncoder[String] = new CsvEncoder[String] { override def encode(value: String): List[String] = List(value) } implicit val intEncoder: CsvEncoder[Int] = new CsvEncoder[Int] { override def encode(value: Int): List[String] = List(value.toString) } implicit val booleanEncoder: CsvEncoder[Boolean] = new CsvEncoder[Boolean] { override def encode(value: Boolean): List[String] = List( if (value) "yes" else "else" ) } implicit def hlistEncoder[H, T <: HList]( implicit hEncoder: CsvEncoder[H], tEncoder: CsvEncoder[T] ): CsvEncoder[H :: T] = new CsvEncoder[H :: T] { override def encode(value: H :: T): List[String] = { value match { case h :: t => hEncoder.encode(h) ++ tEncoder.encode(t) } } } implicit val hnilEncoder: CsvEncoder[HNil] = new CsvEncoder[HNil] { override def encode(value: HNil): List[String] = Nil } implicit def genericEncoder[A, R]( implicit gen: Generic.Aux[A, R], enc: CsvEncoder[R] ): CsvEncoder[A] = new CsvEncoder[A] { override def encode(value: A): List[String] = enc.encode(gen.to(value)) } implicit class CsvEncoderOps[A](value: A) { def asCsv(implicit enc: CsvEncoder[A]): String = enc.encode(value).mkString(",") } } object Main extends App { import CsvEncoder._ case class Person(name: String, age: Int, hat: Boolean) val person = Person("person", 29, true) println(person.asCsv) }
  41. 67.
  42. 68.

    circeͷ࣮ࡍͷίʔυ import io.circe.{ JsonObject, ObjectEncoder } import shapeless.{ LabelledGeneric, Lazy

    } abstract class DerivedObjectEncoder[A] extends ObjectEncoder[A] final object DerivedObjectEncoder { implicit def deriveEncoder[A, R](implicit gen: LabelledGeneric.Aux[A, R], encode: Lazy[ReprObjectEncoder[R]] ): DerivedObjectEncoder[A] = new DerivedObjectEncoder[A] { final def encodeObject(a: A): JsonObject = encode.value.encodeObject(gen.to(a)) } } Encoderͷࣗಈಋग़ʹɺ΄΅ಉ༷ͷίʔυ͕ར༻͞Ε͍ͯΔɻ 
  43. 69.

    Automatic Derivation ࠶ܝ import io.circe.generic.auto._, io.circe.syntax._ case class Person(name: String)

    case class Greeting(salutation: String, person: Person, exclamationMarks: Int) val greeting = Greeting("Hey", Person("Chris"), 3).asJson println(greeting) ᶃ ᶄ ᶅ 1. circeΛimport 2. JSONʹม׵͍ͨ͠case classΛఆٛ 3. JSONʹม׵