Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
circeから学ぶ GenericProgramming入門 - Scala関西Summit ...
Search
Shinichi Morimoto
November 10, 2018
Programming
4.2k
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
460
頑張らないKubernetes/ Real World Kubernetes
shnmorimoto
4
2.2k
Other Decks in Programming
See All in Programming
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
330
FreeBSDでZabbixを動かす
kenkino
0
350
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
5.3k
iOSDC Japan 2026 - Swiftで作って学ぼう!データベース自作入門
kaseken
2
440
機械に任せるテスト、人が見るテスト ⽣成AIで引き直した、運⽤保守フェーズの線引き
dske104
0
140
Vue Fes Japan 2026 タイムテーブル徹底解説
448jp
1
590
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
260
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
220
Intent as Code
shoppingjaws
6
1.1k
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
33
14k
setup-vp GitLab対応の裏側
naokihaba
0
150
JAWS-UG 東京支部が始める、JAWS-UG支部コラボ / JAWS-UG lunchtime LT Collaboration
y0hgi
0
170
Featured
See All Featured
KATA
mclloyd
PRO
35
16k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
76k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
550
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
Odyssey Design
rkendrick25
PRO
2
850
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
250
1.3M
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
590
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
390
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
530
What does AI have to do with Human Rights?
axbom
PRO
1
2.4k
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