Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
circeから学ぶ GenericProgramming入門 - Scala関西Summit ...
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Shinichi Morimoto
November 10, 2018
Programming
4.1k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
circeから学ぶ GenericProgramming入門 - Scala関西Summit 2018
Shinichi Morimoto
November 10, 2018
More Decks by Shinichi Morimoto
See All by Shinichi Morimoto
Actor Model meets the Kubernetes - CNDT 2019
shnmorimoto
6
5.3k
AdTech on Azure - Cosmos DBを利用した配信システムの全て -
shnmorimoto
2
2.8k
Akka Cluster 超入門 - 2019 Fringe81 大新年勉強会
shnmorimoto
1
450
頑張らないKubernetes/ Real World Kubernetes
shnmorimoto
4
2.2k
Other Decks in Programming
See All in Programming
PHPで使える日時の表現と、その知り方 #frontend_phpcon_do
o0h
PRO
0
260
The ROI of Quarkus for Spring Boot Applications
hollycummins
0
140
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
270
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
360
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
160
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
130
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
2
750
AI 時代のソフトウェア設計の学び方
masuda220
PRO
29
13k
エージェンティックRAGにAWSで入門しよう!
har1101
9
1.7k
そのテスト、説明できますか?~LWテスト戦略FW~のご紹介
nakahara
0
160
Performance Engineering for Everyone
elenatanasoiu
0
210
過去最大のMCPアップデート! 2026-07-28 RC版の謎に迫る
licux
6
390
Featured
See All Featured
A better future with KSS
kneath
240
18k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
580
The Mindset for Success: Future Career Progression
greggifford
PRO
0
370
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
620
Into the Great Unknown - MozCon
thekraken
41
2.6k
Designing for Performance
lara
611
70k
Faster Mobile Websites
deanohume
310
32k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
Git: the NoSQL Database
bkeepers
PRO
432
67k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
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