Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

Shinichi Morimoto

November 10, 2018
Tweet

More Decks by Shinichi Morimoto

Other Decks in Programming

Transcript

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

    View Slide

  2. ࣗݾ঺հ
    • ৿ຊ ਅҰ
    • @shnmorimoto
    • Fringe81 גࣜձࣾ
    • αʔόαΠυΤϯδχΞ
    • Scalaྺ1೥൒
    • ؔ੢ग़਎

    View Slide

  3. circe ஌͍ͬͯ·͔͢ʁ

    View Slide

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

    View Slide

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

    View Slide

  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ͷهࡌ͸লུ

    View Slide

  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ʹม׵

    View Slide

  8. ٙ໰
    Ͳ͏ͯ͠import͢Δ͚ͩͰJSON
    ʹม׵Ͱ͖ΔͷͩΖ͏ʁ

    View Slide

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

    View Slide

  10. ࠓ೔࿩͢͜ͱ/࿩͞ͳ͍͜ͱ
    • ࿩͢͜ͱ
    • Generic Programmingʹ͍ͭͯ
    • shapelessͷجຊతͳ࢖͍ํ
    • ࿩͞ͳ͍͜ͱ
    • circeͷৄࡉͳ࢖͍ํ
    • shapelessͷ಺෦࣮૷
    • ଟݴޠʹ͓͚ΔGeneric Programmingͷྫ

    View Slide

  11. ࣄલ஌ࣝ

    View Slide

  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͕͍ͳ͍ʢܧঝ͍ͯ͠ͳ͍ʣ
    ͷʹͳͥ࢖͑ΔͷͩΖ͏ʁ

    View Slide

  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 ܕΫϥε

    View Slide

  14. enrich my libraryύλʔϯ

    JNQMJDJUDMBTTΛఆٛ͢Δ͜ͱʹΑΓɺطଘͷܕʹϝ
    ιουΛ௥Ճ͢Δ͜ͱͰ͖Δ
    implicit class RichString(val value: String) extends AnyVal {
    def exclamation: String = value + "!"
    }
    println("Hello".exclamation)
    ࣮ߦ݁Ռ
    )FMMP

    View Slide

  15. ܕΫϥε

    1FSTPO %PH *OU 1PJOU
    4IPX &R %FCVH +TPO $TW
    ༷ʑͳܕ
    ༷ʑͳৼΔ෣͍
    ܕʹରͯ͠ڞ௨ͷৼΔ෣͍
    Λ௥Ճ͍ͨ͠

    View Slide

  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
    ར༻ྫ

    View Slide

  17. ܕΫϥεͷར༻ʢCSVग़ྗʣ

    def asCsv(value: Any): String = {
    value.????
    }
    BT$TWؔ਺ͷఆٛ
    • ༷ʑͳܕʹద༻Ͱ͖ΔΑ͏ʹAnyܕΛड͚औΔʁ
    • AnyܕͳͷͰɺϝιου͕ඇৗʹݶΒΕΔ…ɻ

    View Slide

  18. ܕΫϥεͷར༻ʢCSVग़ྗʣ
    BT$TWؔ਺ͷఆٛ

    def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ=
    enc.encode(value).mkString(",")

    View Slide

  19. ܕΫϥεͷར༻ʢCSVग़ྗʣ
    BT$TWؔ਺ͷఆٛ

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

    View Slide

  20. ܕΫϥεͷར༻ʢCSVग़ྗʣ
    BT$TWؔ਺ͷఆٛ

    def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ=
    enc.encode(value).mkString(",")
    • ܕΫϥεͷΠϯελϯεΛར༻ͯ͠ɺॲཧΛهड़͢Δ

    View Slide

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

    ܕΫϥε
    ڞ௨ͷৼΔ෣͍ΛΛఆٛ
    ܕΫϥεΠϯελϯε
    ܕຖͷܕΫϥεͷৼΔ෣͍
    ͷ۩ମతͳॲཧΛ࣮૷

    View Slide

  22. ܕΫϥεͷఆٛ
    $47ܕΫϥεΛఆٛ
    • A͕ܕΫϥεͷΠϯελϯεʹͳΔܕʢex. Person, Dog, etc…ʣ
    • AΛड͚औͬͯɺList[String]ʹม׵͢ΔencodeΛఆٛ

    trait CsvEncoder[A] {
    def encode(value: A): List[String]
    }

    View Slide

  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"
    )
    }

    View Slide

  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(",")

    View Slide

  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(",")
    }

    View Slide

  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)
    }

    View Slide

  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 ؔ਺

    View Slide

  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)
    }

    View Slide

  29. ܕΫϥεͷ·ͱΊ
    • ֤ܕʹରͯ͠ɺڞ௨ͷৼΔ෣͍Λ௥Ճ͢Δ͜ͱ͕Ͱ͖Δ
    • ֤ܕʹ͍ͭͯɺܕΫϥεͷΠϯελϯεΛ࣮૷͢Δඞཁ
    ͕͋Δ

    View Slide

  30. Generic Programming

    View Slide

  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͸ܕΫϥεʹΑ࣮ͬͯݱ͞Ε͍ͯΔ͜ͱ͕෼͔ͬͨ

    View Slide

  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͸ར༻Ͱ͖͍ͯΔ
    ෆࢥٞʁ

    View Slide

  33. Generic Programming
    δΣωϦοΫʢ૯শ͋Δ͍͸൚༻ʣϓϩάϥϛϯάʢӳHFOFSJDQSPHSBNNJOHʣ
    ͸σʔλܗࣜʹґଘ͠ͳ͍ίϯϐϡʔλϓϩάϥϛϯάํࣜͰ͋Δɻ
    ग़యϑϦʔඦՊࣄయʰ΢ΟΩϖσΟΞʢ8JLJQFEJBʣʱ
    • C++ͷςϯϓϨʔτ
    • Java/ScalaͷGenerics΋ڱٛͷҙຯͰ͸Generic ProgrammingͷҰछ

    (FOFSJD1SPHSBNNJOHͷྫ

    View Slide

  34. shapeless
    TIBQFMFTTJTBUZQFDMBTTBOEEFQFOEFOUUZQFCBTFEHFOFSJDQSPHSBNNJOH
    MJCSBSZGPS4DBMB
    ग़యIUUQTHJUIVCDPNNJMFTTBCJOTIBQFMFTT
    • Generic ProgrammingΛ࣮ݱ͢Δ Scala Library
    • 2011೥͔ΒMiles Sabin (@milessabin)͞Μ͕։ൃ։࢝
    • HaskellͷScrap Your Boilerplate(SYB)Λ࣮ݱ͍ͯ͠Δ
    • circe, spary-json-shapeless, specs2, etc…Ͱར༻͞Ε͍ͯΔ

    View Slide

  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Λར༻࣮ͯ͠ݱ͍ͯ͠Δ

    View Slide

  36. ͜͜·ͰͷৼΓฦΓ

    ܕΫϥεΛ࢖ͬͯɺ
    ػೳΛ֦ுͰ͖Δͧʂ
    FYBT+TPO

    ར༻͍ͨ͠ܕʹ͍ͭ
    ͯผݸʹΠϯελϯ
    εԽͷ࣮૷Λ͠ͳ͍
    ͱ͍͚ͳ͍ͧʂ
    CPJMFSQMBUF໰୊

    ܕΫϥεͷΠϯελ
    ϯεΛࣗಈతʹಋग़
    Ͱ͖Ε͹ྑ͍ͷͰ͸ʁ

    View Slide

  37. ม׵
    Genericͳσʔλߏ଄

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

    View Slide

  38. ม׵
    Genericͳσʔλߏ଄

    )VNBO
    %PH
    $BU
    (FOFSJDͳܕ
    ۩ମతͳܕʢσʔλߏ଄ʣ Ұൠతͳܕʢσʔλߏ଄ʣ
    TIBQFMFTTͰม׵

    View Slide

  39. shapelessʹΑΔࣗಈಋग़

    View Slide

  40. ࣗಈಋग़·ͰͷྲྀΕ

    ۩ମతͳ
    σʔλߏ଄
    ม׵ ద༻
    Ұൠతͳ
    σʔλߏ଄
    ܕΫϥε

    View Slide

  41. ࣗಈಋग़·ͰͷྲྀΕ

    ۩ମతͳ
    σʔλߏ଄
    ม׵ ద༻
    Ұൠతͳ
    σʔλߏ଄
    ܕΫϥε

    View Slide

  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ܕͷ஋Λ࣋ͭ
    σʔλߏ଄ͱͯ͠ҰൠԽ͢Δ͜ͱ͕Ͱ͖Δ

    View Slide

  43. ListʹΑΔҰൠԽ
    • ListΛར༻ͨ͠ҰൠԽΛݕ౼ͯ͠ΈΔ
    • ஋ΛListʹؚΊΔʹ͜ͱ͕Ͱ͖Δ͕ɺܕ͕Anyʹͳͬͯ͠·͏

    ͦΕͧΕͷܕΛอͬͨ··ɺҰൠԽ͢Δ͜ͱ͕Ͱ͖ͳ
    ͍ͩΖ͏͔ʜɻ
    val anyList: List[Any] = "person" :: 29 :: true :: Nil

    View Slide

  44. HList (Heterogeneous List)
    shapeless͸Genericͳܕͱͯ͠ɺ
    HListʢHeterogeneous ListʣΛఆ͍ٛͯ͠Δ

    Heterogeneous = ҟ࣭ͷ, ҟछͷ, ҟ੒෼͔Β੒Δ

    View Slide

  45. HList (Heterogeneous List)
    ܕΛอͬͨ··ɺҟͳΔܕͷཁૉΛؚΊΔ
    ͜ͱ͕Ͱ͖ΔList

    import shapeless.{::, HNil}
    val personRep: String :: Int :: Boolean :: HNil = "person" :: 29 :: true :: HNil
    4USJOH *OU #PPMFBO )/JM
    HList

    View Slide

  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ͷૢ࡞

    View Slide

  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ʹม׵

    View Slide

  48. ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ

    ۩ମతͳ
    σʔλߏ଄
    ม׵ ద༻
    Ұൠతͳ
    σʔλߏ଄
    ܕΫϥε
    case class, tupleΛҰൠԽͨ͠HListʹม׵

    View Slide

  49. ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ

    ۩ମతͳ
    σʔλߏ଄
    ม׵ ద༻
    Ұൠతͳ
    σʔλߏ଄
    ܕΫϥε
    HList༻ͷܕΫϥεΠϯελϯεΛ४උ

    View Slide

  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"
    )
    }
    }


    View Slide

  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
    }


    View Slide

  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ΛධՁ

    ᶃ ᶄ

    View Slide

  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ग़ྗ

    View Slide

  54. HListͷܕΫϥεΠϯελϯε

    TUSJOH&ODPEFS
    I&ODPEFS lQFSTPOz
    U&ODPEFS USVF)/JM

    IMJTU&ODPEFS


    View Slide

  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





    View Slide

  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







    View Slide

  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








    View Slide

  58. ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ

    ۩ମతͳ
    σʔλߏ଄
    ม׵ ద༻
    Ұൠతͳ
    σʔλߏ଄
    ܕΫϥε
    HList༻ͷܕΫϥεΠϯελϯεΛఆٛ

    View Slide

  59. HListΛར༻ͨ͠Personܕͷಋग़

    ۩ମతͳ
    σʔλߏ଄
    ม׵ ద༻
    Ұൠతͳ
    σʔλߏ଄
    ܕΫϥε
    1FSTPO )-JTU IMJTU&ODPEFS
    QFSTPO&ODPEFS

    View Slide

  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ʹม׵

    View Slide

  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]ͷܕΫϥεΠϯελϯεʹม׵

    View Slide

  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ͳܕΫϥεΠϯελϯε
    6TFSTNPSJNPUP8PSLDJSDF@TBNQMFTSDNBJOTDBMBFYBNQMF)FMMPTDBMBJMMFHBM
    EFQFOEFOUNFUIPEUZQFQBSBNFUFSNBZPOMZCFSFGFSFODFEJOBTVCTFRVFOUQBSBNFUFSTFDUJPO
    HFO(FOFSJD<">
    ?
    POFFSSPSGPVOE
    $PNQJMFDPNQJMF*ODSFNFOUBM
    $PNQJMBUJPOGBJMFE
    ɹίϯύΠϧΤϥʔɹ
    • Ҿ਺͸gen, enc
    • encͷܕͱͯ͠ɺgen.ReprΛར༻͍ͯ͠Δ͕ɺScalaͷจ๏্ɺ͜Ε͸ڐ͞Ε͍ͯͳ͍

    View Slide

  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Λࢦఆ

    View Slide

  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))
    }

    View Slide

  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)

    View Slide

  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)
    }

    View Slide

  67. ܕΫϥεͷࣗಈಋग़

    1FSTPO
    %PH
    $BU
    ϢʔβଆͰఆٛ͢ΔՕॴ ࣄલʹఆ͓ٛͯ͘͠Օॴ
    )-JTU
    HFOFSJD
    &ODPEFS
    IMJTU
    &ODPEFS
    IOJM
    &ODPEFS
    ϓϦϛ
    ςΟϒͳܕ
    &ODPEFS
    -JTU
    <4USJOH>

    View Slide

  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ͷࣗಈಋग़ʹɺ΄΅ಉ༷ͷίʔυ͕ར༻͞Ε͍ͯΔɻ

    View Slide

  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ʹม׵

    View Slide

  70. ٙ໰ɹ࠶ܝ
    Ͳ͏ͯ͠import͢Δ͚ͩͰJSON
    ʹม׵Ͱ͖ΔͷͩΖ͏ʁ

    View Slide

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

    View Slide

  72. ࠓ೔࿩͞ͳ͔ͬͨ͜ͱ
    • CoProductʢEither౳ʹ͓͚ΔGenericͳܕʣ
    • Labelled Generic
    • Tree౳ɺ࠶ؼతͳܕʹ͓͚ΔGenericͳܕ
    • Dependent Type
    • PolyʢHListʹ͓͚Δ Functionalͳૢ࡞ʣ
    • etc, etc…

    View Slide

  73. The Type Astronaut’s Guide to Shapeless

    IUUQTVOEFSTDPSFJPCPPLTTIBQFMFTTHVJEF

    View Slide

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

    -FU`TTDSBQZPVSCPJMFSQMBUF

    View Slide