Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
circeから学ぶ GenericProgramming入門 - Scala関西Summit 2018
Shinichi Morimoto
November 10, 2018
Programming
4
2.5k
circeから学ぶ GenericProgramming入門 - Scala関西Summit 2018
Shinichi Morimoto
November 10, 2018
Tweet
Share
More Decks by Shinichi Morimoto
See All by Shinichi Morimoto
Actor Model meets the Kubernetes - CNDT 2019
shnmorimoto
6
4.3k
AdTech on Azure - Cosmos DBを利用した配信システムの全て -
shnmorimoto
2
2.2k
Akka Cluster 超入門 - 2019 Fringe81 大新年勉強会
shnmorimoto
1
270
頑張らないKubernetes/ Real World Kubernetes
shnmorimoto
4
1.7k
Other Decks in Programming
See All in Programming
Babylon.jsで作ったsceneをレイトレーシングで映えさせる
turamy
1
200
WindowsコンテナDojo:第6回 Red Hat OpenShift入門
oniak3ibm
PRO
0
150
Reactは何を提供するLibraryなのか?
taro28
6
910
ベストプラクティス・ドリフト
sssssssssssshhhhhhhhhh
0
200
WindowsコンテナDojo: 第4回 Red Hat OpenShift Localを使ってみよう
oniak3ibm
PRO
0
170
Pythonで鉄道指向プログラミング
usabarashi
0
110
フロントエンドエンジニアが変える現場のモデリング意識/modeling-awareness-changed-by-front-end-engineers
uggds
32
13k
NestJS_meetup_atamaplus
atamaplus
0
200
20220706_Google Apps Scriptを実演で学ぶ~ GAS × Slack ~
apachan
2
610
大規模プロダクトにLinterを導入し運用している話
hirokiotsuka
0
300
パスワードに関する最近の動向
kenchan0130
1
320
「困りごと」から始める個人開発
ikumatadokoro
4
240
Featured
See All Featured
Agile that works and the tools we love
rasmusluckow
319
19k
5 minutes of I Can Smell Your CMS
philhawksworth
196
18k
Imperfection Machines: The Place of Print at Facebook
scottboms
253
12k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
237
19k
Infographics Made Easy
chrislema
233
17k
The Cult of Friendly URLs
andyhume
68
4.8k
Why You Should Never Use an ORM
jnunemaker
PRO
47
7.6k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
349
27k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
269
12k
StorybookのUI Testing Handbookを読んだ
zakiyama
5
2.5k
A Philosophy of Restraint
colly
192
15k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
151
13k
Transcript
circe͔ΒֶͿ GenericProgrammingೖ ScalaؔSummit 2018 @shnmorimoto
ࣗݾհ • ຊ ਅҰ • @shnmorimoto • Fringe81 גࣜձࣾ •
αʔόαΠυΤϯδχΞ • Scalaྺ1 • ؔग़
circe ͍ͬͯ·͔͢ʁ
circe • JSON library • ݺͼํ • αʔγʔʁΩϧέʔʁ • EncoderͷAutomatic
Derivationʢࣗಈಋग़ʣ • Φϓγϣϯ͕๛Ͱᙱ͍ͱ͜Ζ·Ͱख͕ಧ͘
circe • JSON library • ݺͼํ • αʔγʔʁΩϧέʔʁ • EncoderͷAutomatic
Derivationʢࣗಈಋग़ʣ • Φϓγϣϯ͕๛Ͱᙱ͍ͱ͜Ζ·Ͱख͕ಧ͘
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ͷهࡌলུ
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ʹม
ٙ Ͳ͏ͯ͠import͢Δ͚ͩͰJSON ʹมͰ͖ΔͷͩΖ͏ʁ
ࠓͷΰʔϧ ʮcirceͷEncoderͷࣗಈಋग़ͷΈΛ௨ͯ͠ɺ Generic Programmingʹ͍ͭͯཧղ͢Δʯ
ࠓ͢͜ͱ/͞ͳ͍͜ͱ • ͢͜ͱ • Generic Programmingʹ͍ͭͯ • shapelessͷجຊతͳ͍ํ • ͞ͳ͍͜ͱ
• circeͷৄࡉͳ͍ํ • shapelessͷ෦࣮ • ଟݴޠʹ͓͚ΔGeneric Programmingͷྫ
ࣄલࣝ
ຊʹೖΔલʹ… 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͕͍ͳ͍ʢܧঝ͍ͯ͠ͳ͍ʣ ͷʹͳͥ͑ΔͷͩΖ͏ʁ
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 ܕΫϥε
enrich my libraryύλʔϯ JNQMJDJUDMBTTΛఆٛ͢Δ͜ͱʹΑΓɺطଘͷܕʹϝ ιουΛՃ͢Δ͜ͱͰ͖Δ implicit class RichString(val value:
String) extends AnyVal { def exclamation: String = value + "!" } println("Hello".exclamation) ࣮ߦ݁Ռ )FMMP
ܕΫϥε 1FSTPO %PH *OU 1PJOU 4IPX &R %FCVH +TPO
$TW ༷ʑͳܕ ༷ʑͳৼΔ͍ ܕʹରͯ͠ڞ௨ͷৼΔ͍ ΛՃ͍ͨ͠
ܕΫϥεʢ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 ར༻ྫ
ܕΫϥεͷར༻ʢCSVग़ྗʣ def asCsv(value: Any): String = { value.???? }
BT$TWؔͷఆٛ • ༷ʑͳܕʹద༻Ͱ͖ΔΑ͏ʹAnyܕΛड͚औΔʁ • AnyܕͳͷͰɺϝιου͕ඇৗʹݶΒΕΔ…ɻ
ܕΫϥεͷར༻ʢCSVग़ྗʣ BT$TWؔͷఆٛ def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ= enc.encode(value).mkString(",")
ܕΫϥεͷར༻ʢCSVग़ྗʣ BT$TWؔͷఆٛ def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ= enc.encode(value).mkString(",")
• ར༻͍ͨ͠ܕʹ͍ͭͯimplicitͰͦͷܕΫϥεͷৼΔ͍Λ࣮͠ ͨॲཧΛड͚औΔ • ܕΫϥεͷΠϯελϯεΛҾͰड͚औΔ
ܕΫϥεͷར༻ʢCSVग़ྗʣ BT$TWؔͷఆٛ def asCsv[A](value: A)(implicit enc: CsvEncoder[A]): Stringɹ= enc.encode(value).mkString(",")
• ܕΫϥεͷΠϯελϯεΛར༻ͯ͠ɺॲཧΛهड़͢Δ
ܕΫϥε • ԿΒ͔ͷৼΔ͍Λఆٛ͢ΔΠϯλʔϑΣʔε • ͋ΔܕΫϥεͷΠϯελϯεͰ͋Δܕɺͦͷ ܕΫϥε͕ఆٛ͢ΔৼΔ͍Λ࣮͢Δ • HaskellͰॳΊͯొͨ͠ػೳ ܕΫϥε
ڞ௨ͷৼΔ͍ΛΛఆٛ ܕΫϥεΠϯελϯε ܕຖͷܕΫϥεͷৼΔ͍ ͷ۩ମతͳॲཧΛ࣮
ܕΫϥεͷఆٛ $47ܕΫϥεΛఆٛ • A͕ܕΫϥεͷΠϯελϯεʹͳΔܕʢex. Person, Dog, etc…ʣ • AΛड͚औͬͯɺList[String]ʹม͢ΔencodeΛఆٛ
trait CsvEncoder[A] { def encode(value: A): List[String] }
ܕΫϥεͷΠϯελϯε ܕΫϥεͷΠϯελϯεΛ࡞ 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" ) }
ܕΫϥεͷར༻ $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(",")
ܕΫϥεͷར༻ $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(",") }
ܕΫϥε αϯϓϧίʔυ 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) }
ܕΫϥεΠϝʔδ 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 ؔ
ܧঝΛ͏߹ ܧঝΛར༻ͯ͠ɺಉ༷ͷࣄΛ࣮ݱ 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) }
ܕΫϥεͷ·ͱΊ • ֤ܕʹରͯ͠ɺڞ௨ͷৼΔ͍ΛՃ͢Δ͜ͱ͕Ͱ͖Δ • ֤ܕʹ͍ͭͯɺܕΫϥεͷΠϯελϯεΛ࣮͢Δඞཁ ͕͋Δ
Generic Programming
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ܕΫϥεʹΑ࣮ͬͯݱ͞Ε͍ͯΔ͜ͱ͕͔ͬͨ
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ར༻Ͱ͖͍ͯΔ ෆࢥٞʁ
Generic Programming δΣωϦοΫʢ૯শ͋Δ͍൚༻ʣϓϩάϥϛϯάʢӳHFOFSJDQSPHSBNNJOHʣ σʔλܗࣜʹґଘ͠ͳ͍ίϯϐϡʔλϓϩάϥϛϯάํࣜͰ͋Δɻ ग़యϑϦʔඦՊࣄయʰΟΩϖσΟΞʢ8JLJQFEJBʣʱ • C++ͷςϯϓϨʔτ • Java/ScalaͷGenericsڱٛͷҙຯͰGeneric ProgrammingͷҰछ
(FOFSJD1SPHSBNNJOHͷྫ
shapeless TIBQFMFTTJTBUZQFDMBTTBOEEFQFOEFOUUZQFCBTFEHFOFSJDQSPHSBNNJOH MJCSBSZGPS4DBMB ग़యIUUQTHJUIVCDPNNJMFTTBCJOTIBQFMFTT • Generic ProgrammingΛ࣮ݱ͢Δ Scala Library •
2011͔ΒMiles Sabin (@milessabin)͞Μ͕։ൃ։࢝ • HaskellͷScrap Your Boilerplate(SYB)Λ࣮ݱ͍ͯ͠Δ • circe, spary-json-shapeless, specs2, etc…Ͱར༻͞Ε͍ͯΔ
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Λར༻࣮ͯ͠ݱ͍ͯ͠Δ
͜͜·ͰͷৼΓฦΓ ܕΫϥεΛͬͯɺ ػೳΛ֦ுͰ͖Δͧʂ FYBT+TPO ར༻͍ͨ͠ܕʹ͍ͭ ͯผݸʹΠϯελϯ εԽͷ࣮Λ͠ͳ͍ ͱ͍͚ͳ͍ͧʂ CPJMFSQMBUF
ܕΫϥεͷΠϯελ ϯεΛࣗಈతʹಋग़ Ͱ͖Εྑ͍ͷͰʁ
ม Genericͳσʔλߏ )VNBO %PH $BU (FOFSJDͳܕ ۩ମతͳܕʢσʔλߏʣ Ұൠతͳܕʢσʔλߏʣ (FOFSJDͳ
ܕʹରͯ͠Πϯελϯ εΛҰͭ༻ҙ͓͚ͯ͠ ྑ͍
ม Genericͳσʔλߏ )VNBO %PH $BU (FOFSJDͳܕ ۩ମతͳܕʢσʔλߏʣ Ұൠతͳܕʢσʔλߏʣ TIBQFMFTTͰม
shapelessʹΑΔࣗಈಋग़
ࣗಈಋग़·ͰͷྲྀΕ ۩ମతͳ σʔλߏ ม ద༻ Ұൠతͳ σʔλߏ ܕΫϥε
ࣗಈಋग़·ͰͷྲྀΕ ۩ମతͳ σʔλߏ ม ద༻ Ұൠతͳ σʔλߏ ܕΫϥε
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) • PersonStringܕ, Intܕ, BooleanܕͷϑΟʔϧυΛ͍࣋ͬͯΔ • FoodStringܕ, Intܕ, BooleanܕͷϑΟʔϧυΛ͍࣋ͬͯΔ • ্هྫͷtupleStringܕ, Intܕ, BooleanܕΛ͍࣋ͬͯΔ DBTFDMBTTͱUVQMF99ܕͷΛ࣋ͭ σʔλߏͱͯ͠ҰൠԽ͢Δ͜ͱ͕Ͱ͖Δ
ListʹΑΔҰൠԽ • ListΛར༻ͨ͠ҰൠԽΛݕ౼ͯ͠ΈΔ • ΛListʹؚΊΔʹ͜ͱ͕Ͱ͖Δ͕ɺܕ͕Anyʹͳͬͯ͠·͏ ͦΕͧΕͷܕΛอͬͨ··ɺҰൠԽ͢Δ͜ͱ͕Ͱ͖ͳ ͍ͩΖ͏͔ʜɻ val anyList:
List[Any] = "person" :: 29 :: true :: Nil
HList (Heterogeneous List) shapelessGenericͳܕͱͯ͠ɺ HListʢHeterogeneous ListʣΛఆ͍ٛͯ͠Δ Heterogeneous = ҟ࣭ͷ,
ҟछͷ, ҟ͔ΒΔ
HList (Heterogeneous List) ܕΛอͬͨ··ɺҟͳΔܕͷཁૉΛؚΊΔ ͜ͱ͕Ͱ͖ΔList import shapeless.{::, HNil} val
personRep: String :: Int :: Boolean :: HNil = "person" :: 29 :: true :: HNil 4USJOH *OU #PPMFBO )/JM HList
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ͷૢ࡞
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ʹม
ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ ۩ମతͳ σʔλߏ ม ద༻ Ұൠతͳ σʔλߏ ܕΫϥε
case class, tupleΛҰൠԽͨ͠HListʹม
ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ ۩ମతͳ σʔλߏ ม ద༻ Ұൠతͳ σʔλߏ ܕΫϥε
HList༻ͷܕΫϥεΠϯελϯεΛ४උ
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" ) } } ᶃ ᶄ
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 } ᶃ ᶄ
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ΛධՁ ᶃ ᶄ
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ग़ྗ
HListͷܕΫϥεΠϯελϯε TUSJOH&ODPEFS I&ODPEFS lQFSTPOz U&ODPEFS USVF)/JM IMJTU&ODPEFS ᶃ
HListͷܕΫϥεΠϯελϯε TUSJOH&ODPEFS I&ODPEFS lQFSTPOz U&ODPEFS USVF)/JM IMJTU&ODPEFS JOU&ODPEFS I&ODPEFS
U&ODPEFS USVF)/JM IMJTU&ODPEFS -JTU lQFSTPOz ᶃ ᶄ
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 ᶃ ᶄ ᶅ
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 ᶃ ᶄ ᶅ ᶆ
ࣗಈಋग़·ͰͷྲྀΕ ࠶ܝ ۩ମతͳ σʔλߏ ม ద༻ Ұൠతͳ σʔλߏ ܕΫϥε
HList༻ͷܕΫϥεΠϯελϯεΛఆٛ
HListΛར༻ͨ͠Personܕͷಋग़ ۩ମతͳ σʔλߏ ม ద༻ Ұൠతͳ σʔλߏ ܕΫϥε 1FSTPO
)-JTU IMJTU&ODPEFS QFSTPO&ODPEFS
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ʹม
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]ͷܕΫϥεΠϯελϯεʹม
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ͷจ๏্ɺ͜Εڐ͞Ε͍ͯͳ͍
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Λࢦఆ
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)) }
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)
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) }
ܕΫϥεͷࣗಈಋग़ 1FSTPO %PH $BU ϢʔβଆͰఆٛ͢ΔՕॴ ࣄલʹఆ͓ٛͯ͘͠Օॴ )-JTU HFOFSJD &ODPEFS
IMJTU &ODPEFS IOJM &ODPEFS ϓϦϛ ςΟϒͳܕ &ODPEFS -JTU <4USJOH>
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ͷࣗಈಋग़ʹɺ΄΅ಉ༷ͷίʔυ͕ར༻͞Ε͍ͯΔɻ
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ʹม
ٙɹ࠶ܝ Ͳ͏ͯ͠import͢Δ͚ͩͰJSON ʹมͰ͖ΔͷͩΖ͏ʁ
͑ shapelessͰܕΫϥεΛࣗಈత ʹಋग़͍ͯ͠Δ͔Β
ࠓ͞ͳ͔ͬͨ͜ͱ • CoProductʢEitherʹ͓͚ΔGenericͳܕʣ • Labelled Generic • Treeɺ࠶ؼతͳܕʹ͓͚ΔGenericͳܕ • Dependent
Type • PolyʢHListʹ͓͚Δ Functionalͳૢ࡞ʣ • etc, etc…
The Type Astronaut’s Guide to Shapeless IUUQTVOEFSTDPSFJPCPPLTTIBQFMFTTHVJEF
·ͱΊ • circeΛimport͢Δ͚ͩͰɺEncoderΛఆٛͤͣʹJSONʹม͢Δ ͜ͱ͕Ͱ͖Δɻ • circeͰshapelessΛͬͯɺEncoderͷࣗಈಋग़Λ࣮ݱ͍ͯ͠Δ • shapelessΛ͏͜ͱʹΑΓɺେྔͷboilerplateΛ໓͢Δ͜ͱ͕ Ͱ͖Δ
-FU`TTDSBQZPVSCPJMFSQMBUF