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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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
460
頑張らないKubernetes/ Real World Kubernetes
shnmorimoto
4
2.2k
Other Decks in Programming
See All in Programming
App Storeの外へ──日本のiOSサイドローディング入門 for iOSDC Japan 2026
yuukiw00w
0
230
AI × TiDD / 2026.09.05 Redmine 大阪
tokudiro
1
180
The Rails Doctrine Decade
koic
2
200
FreeBSDでZabbixを動かす
kenkino
0
310
マイコン向けの軽量Ruby「PicoRuby」で各種デバイスを制御するネイティブアプリの実現手法
bash0c7
0
430
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
2
290
モジュールの視点からSwiftを読み解く #iosdc
s_shimotori
0
190
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
7
2.1k
AGENTS.md Is Not Enough:Build Skills, Don't Download Them
lx_t
0
130
The Good Stuff, Not the Slop: Engineering High-Quality Android Apps with Modern AI Tooling
danybony
1
260
Vibes Containers 〜AIで変わるコンテナ設計と運用〜
tkikuc
3
550
XP祭りでしか伝わらないフリップネタ #xpjug
murabayashi
0
150
Featured
See All Featured
The Cult of Friendly URLs
andyhume
79
7k
Embracing the Ebb and Flow
colly
88
5.2k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
460
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
280
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
Believing is Seeing
oripsolob
1
220
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
400
Product Roadmaps are Hard
iamctodd
55
13k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
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