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

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

1b963e66060bcc77b7ed647f5b6ff3a7?s=128

Shinichi Morimoto

November 10, 2018
Tweet

Transcript

  1. circe͔ΒֶͿ GenericProgrammingೖ໳ Scalaؔ੢Summit 2018 @shnmorimoto 

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

    αʔόαΠυΤϯδχΞ • Scalaྺ1೥൒ • ؔ੢ग़਎ 
  3. circe ஌͍ͬͯ·͔͢ʁ 

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

    Derivationʢࣗಈಋग़ʣ • Φϓγϣϯ͕๛෋Ͱᙱ͍ͱ͜Ζ·Ͱख͕ಧ͘ 
  5. circe • JSON library • ݺͼํ • αʔγʔʁΩϧέʔʁ • EncoderͷAutomatic

    Derivationʢࣗಈಋग़ʣ • Φϓγϣϯ͕๛෋Ͱᙱ͍ͱ͜Ζ·Ͱख͕ಧ͘ 
  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ͷهࡌ͸লུ
  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ʹม׵ 
  8. ٙ໰ Ͳ͏ͯ͠import͢Δ͚ͩͰJSON ʹม׵Ͱ͖ΔͷͩΖ͏ʁ 

  9. ࠓ೔ͷΰʔϧ ʮcirceͷEncoderͷࣗಈಋग़ͷ࢓૊ΈΛ௨ͯ͠ɺ Generic Programmingʹ͍ͭͯཧղ͢Δʯ 

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

    • circeͷৄࡉͳ࢖͍ํ • shapelessͷ಺෦࣮૷ • ଟݴޠʹ͓͚ΔGeneric Programmingͷྫ 
  11. ࣄલ஌ࣝ 

  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͕͍ͳ͍ʢܧঝ͍ͯ͠ͳ͍ʣ ͷʹͳͥ࢖͑ΔͷͩΖ͏ʁ 
  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 ܕΫϥε
  14. enrich my libraryύλʔϯ  JNQMJDJUDMBTTΛఆٛ͢Δ͜ͱʹΑΓɺطଘͷܕʹϝ ιουΛ௥Ճ͢Δ͜ͱͰ͖Δ implicit class RichString(val value:

    String) extends AnyVal { def exclamation: String = value + "!" } println("Hello".exclamation) ࣮ߦ݁Ռ )FMMP
  15. ܕΫϥε  1FSTPO %PH *OU 1PJOU 4IPX &R %FCVH +TPO

    $TW ༷ʑͳܕ ༷ʑͳৼΔ෣͍ ܕʹରͯ͠ڞ௨ͷৼΔ෣͍ Λ௥Ճ͍ͨ͠
  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 ར༻ྫ
  17. ܕΫϥεͷར༻ʢCSVग़ྗʣ  def asCsv(value: Any): String = { value.???? }

    BT$TWؔ਺ͷఆٛ • ༷ʑͳܕʹద༻Ͱ͖ΔΑ͏ʹAnyܕΛड͚औΔʁ • AnyܕͳͷͰɺϝιου͕ඇৗʹݶΒΕΔ…ɻ
  18. ܕΫϥεͷར༻ʢCSVग़ྗʣ BT$TWؔ਺ͷఆٛ  def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ= enc.encode(value).mkString(",")

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

    • ར༻͍ͨ͠ܕʹ͍ͭͯimplicitͰͦͷܕΫϥεͷৼΔ෣͍Λ࣮૷͠ ͨॲཧΛड͚औΔ • ܕΫϥεͷΠϯελϯεΛҾ਺Ͱड͚औΔ
  20. ܕΫϥεͷར༻ʢCSVग़ྗʣ BT$TWؔ਺ͷఆٛ  def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ= enc.encode(value).mkString(",")

    • ܕΫϥεͷΠϯελϯεΛར༻ͯ͠ɺॲཧΛهड़͢Δ
  21. ܕΫϥε • ԿΒ͔ͷৼΔ෣͍Λఆٛ͢ΔΠϯλʔϑΣʔε • ͋ΔܕΫϥεͷΠϯελϯεͰ͋Δܕ͸ɺͦͷ ܕΫϥε͕ఆٛ͢ΔৼΔ෣͍Λ࣮૷͢Δ • HaskellͰॳΊͯొ৔ͨ͠ػೳ  ܕΫϥε

    ڞ௨ͷৼΔ෣͍ΛΛఆٛ ܕΫϥεΠϯελϯε ܕຖͷܕΫϥεͷৼΔ෣͍ ͷ۩ମతͳॲཧΛ࣮૷
  22. ܕΫϥεͷఆٛ $47ܕΫϥεΛఆٛ • A͕ܕΫϥεͷΠϯελϯεʹͳΔܕʢex. Person, Dog, etc…ʣ • AΛड͚औͬͯɺList[String]ʹม׵͢ΔencodeΛఆٛ 

    trait CsvEncoder[A] { def encode(value: A): List[String] }
  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" ) }
  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(",")
  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(",") }
  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) }
  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 ؔ਺
  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) }
  29. ܕΫϥεͷ·ͱΊ • ֤ܕʹରͯ͠ɺڞ௨ͷৼΔ෣͍Λ௥Ճ͢Δ͜ͱ͕Ͱ͖Δ • ֤ܕʹ͍ͭͯɺܕΫϥεͷΠϯελϯεΛ࣮૷͢Δඞཁ ͕͋Δ 

  30. Generic Programming 

  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͸ܕΫϥεʹΑ࣮ͬͯݱ͞Ε͍ͯΔ͜ͱ͕෼͔ͬͨ 
  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͸ར༻Ͱ͖͍ͯΔ ෆࢥٞʁ
  33. Generic Programming δΣωϦοΫʢ૯শ͋Δ͍͸൚༻ʣϓϩάϥϛϯάʢӳHFOFSJDQSPHSBNNJOHʣ ͸σʔλܗࣜʹґଘ͠ͳ͍ίϯϐϡʔλϓϩάϥϛϯάํࣜͰ͋Δɻ ग़యϑϦʔඦՊࣄయʰ΢ΟΩϖσΟΞʢ8JLJQFEJBʣʱ • C++ͷςϯϓϨʔτ • Java/ScalaͷGenerics΋ڱٛͷҙຯͰ͸Generic ProgrammingͷҰछ

     (FOFSJD1SPHSBNNJOHͷྫ
  34. shapeless TIBQFMFTTJTBUZQFDMBTTBOEEFQFOEFOUUZQFCBTFEHFOFSJDQSPHSBNNJOH MJCSBSZGPS4DBMB ग़యIUUQTHJUIVCDPNNJMFTTBCJOTIBQFMFTT • Generic ProgrammingΛ࣮ݱ͢Δ Scala Library •

    2011೥͔ΒMiles Sabin (@milessabin)͞Μ͕։ൃ։࢝ • HaskellͷScrap Your Boilerplate(SYB)Λ࣮ݱ͍ͯ͠Δ • circe, spary-json-shapeless, specs2, etc…Ͱར༻͞Ε͍ͯΔ 
  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Λར༻࣮ͯ͠ݱ͍ͯ͠Δ
  36. ͜͜·ͰͷৼΓฦΓ  ܕΫϥεΛ࢖ͬͯɺ ػೳΛ֦ுͰ͖Δͧʂ FYBT+TPO ར༻͍ͨ͠ܕʹ͍ͭ ͯผݸʹΠϯελϯ εԽͷ࣮૷Λ͠ͳ͍ ͱ͍͚ͳ͍ͧʂ CPJMFSQMBUF໰୊

    ܕΫϥεͷΠϯελ ϯεΛࣗಈతʹಋग़ Ͱ͖Ε͹ྑ͍ͷͰ͸ʁ
  37. ม׵ Genericͳσʔλߏ଄  )VNBO %PH $BU (FOFSJDͳܕ ۩ମతͳܕʢσʔλߏ଄ʣ Ұൠతͳܕʢσʔλߏ଄ʣ (FOFSJDͳ

    ܕʹରͯ͠Πϯελϯ εΛҰͭ༻ҙ͓͚ͯ͠ ͹ྑ͍
  38. ม׵ Genericͳσʔλߏ଄  )VNBO %PH $BU (FOFSJDͳܕ ۩ମతͳܕʢσʔλߏ଄ʣ Ұൠతͳܕʢσʔλߏ଄ʣ TIBQFMFTTͰม׵

  39. shapelessʹΑΔࣗಈಋग़ 

  40. ࣗಈಋग़·ͰͷྲྀΕ  ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε

  41. ࣗಈಋग़·ͰͷྲྀΕ  ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε

  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ܕͷ஋Λ࣋ͭ σʔλߏ଄ͱͯ͠ҰൠԽ͢Δ͜ͱ͕Ͱ͖Δ
  43. ListʹΑΔҰൠԽ • ListΛར༻ͨ͠ҰൠԽΛݕ౼ͯ͠ΈΔ • ஋ΛListʹؚΊΔʹ͜ͱ͕Ͱ͖Δ͕ɺܕ͕Anyʹͳͬͯ͠·͏  ͦΕͧΕͷܕΛอͬͨ··ɺҰൠԽ͢Δ͜ͱ͕Ͱ͖ͳ ͍ͩΖ͏͔ʜɻ val anyList:

    List[Any] = "person" :: 29 :: true :: Nil
  44. HList (Heterogeneous List) shapeless͸Genericͳܕͱͯ͠ɺ HListʢHeterogeneous ListʣΛఆ͍ٛͯ͠Δ  Heterogeneous = ҟ࣭ͷ,

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

    personRep: String :: Int :: Boolean :: HNil = "person" :: 29 :: true :: HNil 4USJOH *OU #PPMFBO )/JM HList
  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ͷૢ࡞
  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ʹม׵ 
  48. ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ  ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε

    case class, tupleΛҰൠԽͨ͠HListʹม׵
  49. ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ  ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε

    HList༻ͷܕΫϥεΠϯελϯεΛ४උ
  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" ) } } ᶃ ᶄ
  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 } ᶃ ᶄ
  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ΛධՁ  ᶃ ᶄ
  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ग़ྗ 
  54. HListͷܕΫϥεΠϯελϯε  TUSJOH&ODPEFS I&ODPEFS lQFSTPOz U&ODPEFS USVF)/JM IMJTU&ODPEFS ᶃ

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

     U&ODPEFS USVF)/JM IMJTU&ODPEFS -JTU lQFSTPOz ᶃ ᶄ
  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 ᶃ ᶄ ᶅ
  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 ᶃ ᶄ ᶅ ᶆ
  58. ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ  ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε

    HList༻ͷܕΫϥεΠϯελϯεΛఆٛ
  59. HListΛར༻ͨ͠Personܕͷಋग़  ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε 1FSTPO

    )-JTU IMJTU&ODPEFS QFSTPO&ODPEFS
  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ʹม׵
  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]ͷܕΫϥεΠϯελϯεʹม׵
  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ͷจ๏্ɺ͜Ε͸ڐ͞Ε͍ͯͳ͍ 
  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Λࢦఆ 
  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)) }
  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)
  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) }
  67. ܕΫϥεͷࣗಈಋग़  1FSTPO %PH $BU ϢʔβଆͰఆٛ͢ΔՕॴ ࣄલʹఆ͓ٛͯ͘͠Օॴ )-JTU HFOFSJD &ODPEFS

    IMJTU &ODPEFS IOJM &ODPEFS ϓϦϛ ςΟϒͳܕ &ODPEFS -JTU <4USJOH>
  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ͷࣗಈಋग़ʹɺ΄΅ಉ༷ͷίʔυ͕ར༻͞Ε͍ͯΔɻ 
  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ʹม׵ 
  70. ٙ໰ɹ࠶ܝ Ͳ͏ͯ͠import͢Δ͚ͩͰJSON ʹม׵Ͱ͖ΔͷͩΖ͏ʁ 

  71. ౴͑ shapelessͰܕΫϥεΛࣗಈత ʹಋग़͍ͯ͠Δ͔Β 

  72. ࠓ೔࿩͞ͳ͔ͬͨ͜ͱ • CoProductʢEither౳ʹ͓͚ΔGenericͳܕʣ • Labelled Generic • Tree౳ɺ࠶ؼతͳܕʹ͓͚ΔGenericͳܕ • Dependent

    Type • PolyʢHListʹ͓͚Δ Functionalͳૢ࡞ʣ • etc, etc… 
  73. The Type Astronaut’s Guide to Shapeless  IUUQTVOEFSTDPSFJPCPPLTTIBQFMFTTHVJEF

  74. ·ͱΊ • circeΛimport͢Δ͚ͩͰɺEncoderΛఆٛͤͣʹJSONʹม׵͢Δ ͜ͱ͕Ͱ͖Δɻ • circeͰ͸shapelessΛ࢖ͬͯɺEncoderͷࣗಈಋग़Λ࣮ݱ͍ͯ͠Δ • shapelessΛ࢖͏͜ͱʹΑΓɺେྔͷboilerplateΛ๾໓͢Δ͜ͱ͕ Ͱ͖Δ 

    -FU`TTDSBQZPVSCPJMFSQMBUF