Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

circe ஌͍ͬͯ·͔͢ʁ

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

ࣄલ஌ࣝ

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

ܕΫϥεʢ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 ར༻ྫ

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

ܕΫϥεͷར༻ʢCSVग़ྗʣ BT$TWؔ਺ͷఆٛ def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ= enc.encode(value).mkString(",") • ܕΫϥεͷΠϯελϯεΛར༻ͯ͠ɺॲཧΛهड़͢Δ

Slide 21

Slide 21 text

ܕΫϥε • ԿΒ͔ͷৼΔ෣͍Λఆٛ͢ΔΠϯλʔϑΣʔε • ͋ΔܕΫϥεͷΠϯελϯεͰ͋Δܕ͸ɺͦͷ ܕΫϥε͕ఆٛ͢ΔৼΔ෣͍Λ࣮૷͢Δ • HaskellͰॳΊͯొ৔ͨ͠ػೳ ܕΫϥε ڞ௨ͷৼΔ෣͍ΛΛఆٛ ܕΫϥεΠϯελϯε ܕຖͷܕΫϥεͷৼΔ෣͍ ͷ۩ମతͳॲཧΛ࣮૷

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

ܕΫϥεͷΠϯελϯε ܕΫϥεͷΠϯελϯεΛ࡞੒ 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" ) }

Slide 24

Slide 24 text

ܕΫϥεͷར༻ $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(",")

Slide 25

Slide 25 text

ܕΫϥεͷར༻ $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(",") }

Slide 26

Slide 26 text

ܕΫϥε αϯϓϧίʔυ 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) }

Slide 27

Slide 27 text

ܕΫϥεΠϝʔδ 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 ؔ਺

Slide 28

Slide 28 text

ܧঝΛ࢖͏৔߹ ܧঝΛར༻ͯ͠ɺಉ༷ͷࣄΛ࣮ݱ 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) }

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Generic Programming

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

͜͜·ͰͷৼΓฦΓ ܕΫϥεΛ࢖ͬͯɺ ػೳΛ֦ுͰ͖Δͧʂ FYBT+TPO ར༻͍ͨ͠ܕʹ͍ͭ ͯผݸʹΠϯελϯ εԽͷ࣮૷Λ͠ͳ͍ ͱ͍͚ͳ͍ͧʂ CPJMFSQMBUF໰୊ ܕΫϥεͷΠϯελ ϯεΛࣗಈతʹಋग़ Ͱ͖Ε͹ྑ͍ͷͰ͸ʁ

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

shapelessʹΑΔࣗಈಋग़

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

ListʹΑΔҰൠԽ • ListΛར༻ͨ͠ҰൠԽΛݕ౼ͯ͠ΈΔ • ஋ΛListʹؚΊΔʹ͜ͱ͕Ͱ͖Δ͕ɺܕ͕Anyʹͳͬͯ͠·͏ ͦΕͧΕͷܕΛอͬͨ··ɺҰൠԽ͢Δ͜ͱ͕Ͱ͖ͳ ͍ͩΖ͏͔ʜɻ val anyList: List[Any] = "person" :: 29 :: true :: Nil

Slide 44

Slide 44 text

HList (Heterogeneous List) shapeless͸Genericͳܕͱͯ͠ɺ HListʢHeterogeneous ListʣΛఆ͍ٛͯ͠Δ Heterogeneous = ҟ࣭ͷ, ҟछͷ, ҟ੒෼͔Β੒Δ

Slide 45

Slide 45 text

HList (Heterogeneous List) ܕΛอͬͨ··ɺҟͳΔܕͷཁૉΛؚΊΔ ͜ͱ͕Ͱ͖ΔList import shapeless.{::, HNil} val personRep: String :: Int :: Boolean :: HNil = "person" :: 29 :: true :: HNil 4USJOH *OU #PPMFBO )/JM HList

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε HList༻ͷܕΫϥεΠϯελϯεΛ४උ

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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 } ᶃ ᶄ

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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 ᶃ ᶄ ᶅ

Slide 57

Slide 57 text

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 ᶃ ᶄ ᶅ ᶆ

Slide 58

Slide 58 text

ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε HList༻ͷܕΫϥεΠϯελϯεΛఆٛ

Slide 59

Slide 59 text

HListΛར༻ͨ͠Personܕͷಋग़ ۩ମతͳ σʔλߏ଄ ม׵ ద༻ Ұൠతͳ σʔλߏ଄ ܕΫϥε 1FSTPO )-JTU IMJTU&ODPEFS QFSTPO&ODPEFS

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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ͷจ๏্ɺ͜Ε͸ڐ͞Ε͍ͯͳ͍

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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)

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

The Type Astronaut’s Guide to Shapeless IUUQTVOEFSTDPSFJPCPPLTTIBQFMFTTHVJEF

Slide 74

Slide 74 text

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