Slide 1

Slide 1 text

cats in practice ੨ࢁΤϯδχΞษڧަྲྀձʙScalaษڧձʙ ؔ਺ܕϥΠϒϥϦcatsͷΞυςΫ࣮ӡ༻Ͱͷ࢖༻ྫ

Slide 2

Slide 2 text

ञҪ ਸࢸ - ౦େӃ ৘ใཧ޻ தୀ
 - 2016/01- F@N Communicationsגࣜձࣾ - Scala Engineer / Data Scientist - Slack & Raspberry PiͰΤΞίϯ͚ͭͨΓ

Slide 3

Slide 3 text

લఏͱ͍ͯ͠Δ஌ࣝ - جຊతͳScalaͷ஌ࣝ - ߏจ - Option, List, Either, TryͳͲͷ
 جຊతͳϞφυͷѻ͍ํ

Slide 4

Slide 4 text

࿩͞ͳ͍͜ͱ - ਂ͍ؔ਺ܕϓϩάϥϛϯάͷ࿩ - ϞφϞφͨ͜͠ͱͱ͔ - IOϞφυͱ͔

Slide 5

Slide 5 text

What is cats?

Slide 6

Slide 6 text

GitHub

Slide 7

Slide 7 text

GitHub ελʔ਺͸ͪΐ͍

Slide 8

Slide 8 text

catsͱ͸ - Scalaʹ͓͚Δؔ਺ܕϥΠϒϥϦͷσϑΝΫτελϯμʔυ - ҎԼͷΑ͏ͳؔ਺ܕ৭ͷڧ͍ϥΠϒϥϦ͕಺෦Ͱ
 catsʹґଘ͍ͯ͠Δ - circeʢJSONॲཧʣ - doobieʢDBΞΫηεʣ - finchʢHTTPϧʔςΟϯάʣ - refinedʢม਺ͷ஋ʹΑΔݫ֨ͳܕ੍໿ʣ

Slide 9

Slide 9 text

঺հ͢Δ΋ͷ - syntax.option - syntax.either - NonEmptyList - sequence, traverse - OptionT, EitherT

Slide 10

Slide 10 text

syntax.option

Slide 11

Slide 11 text

syntax.option._ // ී௨ val i: Option[Int] = Some(1) val j: Option[Int] = None —————————————————————————————— // cats import cats.syntax.option._ val i: Option[Int] = 1.some val j: Option[Int] = none

Slide 12

Slide 12 text

syntax.either

Slide 13

Slide 13 text

syntax.either._ val a2c : A => C val b2c : B => C val b2d : B => D val either : Either[A, B] either.leftMap(a2C) : Either[C, B] either.bimap(a2c, b2d) : Either[C, D] either.bifoldMap(a2c, b2c) : C

Slide 14

Slide 14 text

“leftMap” in practice // jsonจࣈྻΛܕAʹύʔε͢Δɻࣦഊ͢Δͱͦͷཧ༝Λॻ͍ͨ String͕ฦͬͯ͘Δɻ
 val either: Either[String, A] = Json.decode(str) // StringͰ͸ҙຯ͕ᐆດͳͷͰɺErrorܕʹͯ͠Left͕Ͳ͏͍ ͏ҙຯͳͷ͔໌֬ʹ͢Δɻ either.leftMap(s => new CustomError(s)) : Either[CustomError, A]

Slide 15

Slide 15 text

“bifoldMap” in practice // ഑৴͍ͨ͠޿ࠂʢAdʣΛऔಘ͢Δɻ΋͠ͳ͍৔߹͸ɺԿނͳ͍ͷ͔ ͦͷཧ༝Λҙຯ͢ΔNoAdReason͕ฦͬͯ͘Δɻ val result: Either[NoAdReason, Ad] = getAd // ݁ՌΛϩάʹྲྀؔ͢਺ɻ੒ޭ͢Δͱtrueɻ val logNoAdReason: NoAdReason => Boolean val logAd : Ad => Boolean // ϩάʹྲྀ͢ val succeeded = result.bifoldMap(logNoAdReason, logAd)

Slide 16

Slide 16 text

NonEmptyList

Slide 17

Slide 17 text

NonEmptyList (a.k.a “Nel”) - ۭͰ͸ͳ͍͜ͱ͕อূ͞Ε͍ͯΔList scala> import cats.syntax.list._ scala> List(1,2,3).toNel res0: Option[cats.data.NonEmptyList[Int]] = Some(NonEmptyList(1, 2, 3)) scala> List.empty[Int].toNel res1: Option[cats.data.NonEmptyList[Int]] = None

Slide 18

Slide 18 text

List vs NonEmptyList val list : List[Int] list.head : Int // unsafe list.tail : List[Int] // unsafe list.headOption : Option[Int] // safe val nel : NonEmptyList[Int] nel.head : Int // safe nel.tail : List[Int] // safe

Slide 19

Slide 19 text

“NonEmptyList” in practice def getCandidateAds: List[Ad] // ഑৴Ͱ͖Δ޿ࠂʢAdʣ def getAdditionalInfo: Option[Info] // ࠷దͳ޿ࠂΛܾΊΔͨΊͷ৘ใ def chooseBestAd(info: Info, ads: NonEmptyList[Ad]): Ad for { adNel <- getCandidateAds.toNel info <- getAdditionalInfoɹ// ഑৴Ͱ͖Δ޿ࠂ͕ͳ͚Ε͹ݺ͹Εͳ͍ } yield chooseBestAd(info, adNel)

Slide 20

Slide 20 text

sequence, traverse

Slide 21

Slide 21 text

sequence, traverse scala> import cats.implicits._ scala> (List(Some(1), Some(2)): List[Option[Int]]).sequence res0: Option[List[Int]] = Some(List(1, 2)) scala> (List(Some(1), None): List[Option[Int]]).sequence res1: Option[List[Int]] = None scala> (Some(List(1,2,3)): Option[List[Int]]).sequence res2: List[Option[Int]] = List(Some(1), Some(2), Some(3)) scala> (None: Option[List[Int]]).sequence res3: List[Option[Int]] = List(None) - 2ॏʹแ·ΕͨMonadͷॱ൪Λͻͬ͘Γฦ͢

Slide 22

Slide 22 text

sequence, traverse scala> import cats.implicits._ scala> (List(Some(1), Some(2)): List[Option[Int]]).sequence res0: Option[List[Int]] = Some(List(1, 2)) scala> (List(Some(1), None): List[Option[Int]]).sequence res1: Option[List[Int]] = None scala> (Some(List(1,2,3)): Option[List[Int]]).sequence res2: List[Option[Int]] = List(Some(1), Some(2), Some(3)) scala> (None: Option[List[Int]]).sequence res3: List[Option[Int]] = List(None) - 2ॏʹแ·ΕͨMonadͷॱ൪Λͻͬ͘Γฦ͢

Slide 23

Slide 23 text

“sequence” in practice // csvͷ֤ߦΛAʹม׵ɻ1ߦͰ΋ࣦഊͨ͠ΒɺશମΛࣦഊʹ͢Δ def parseCsv( csv: List[String], parse: String => Try[A], ): Try[List[A]] = { val parsed: List[Try[A]] = csv.map(parse) parsed.sequence }

Slide 24

Slide 24 text

ύοͱݟɺΑͦ͞͏͚ͩͲ…

Slide 25

Slide 25 text

“sequence” in practice // csvͷ֤ߦΛAʹม׵ɻ1ߦͰ΋ࣦഊͨ͠ΒɺશମΛࣦഊʹ͢Δ def parseCsv( csv: List[String], parse: String => Try[A], ): Try[List[A]] = { val parsed: List[Try[A]] = csv.map(parse) parsed.sequence } ߦ໨Ͱࣦഊ͢Δ৔߹Ͱ΋ɺશߦQBSTFͯ͠͠·͏

Slide 26

Slide 26 text

͜͏͍͏৔߹͸traverseʂ

Slide 27

Slide 27 text

“traverse” in practice // csvͷ֤ߦΛAʹม׵ɻ1ߦͰ΋ࣦഊͨ͠ΒɺશମΛࣦഊʹ͢Δ def parseCsv( csv: List[String], parse: String => Try[A], ): Try[List[A]] = { // ࣦഊͨ͠ߦ͕͋ͬͨΒͦͷ࣌఺Ͱऴྃ csv.traverse(parse) }

Slide 28

Slide 28 text

OptionT, EitherT

Slide 29

Slide 29 text

OptionT, EitherT // Redis͔ΒURLΛऔಘ def getFromRedis(key: String): Future[Option[String]] // ͦͷURLʹHTTPΞΫηεͯ͠IDΛऔಘ def getViaHttp(url: String): Future[Int] // ͦͷIDͰAΛݟ͚ͭΔ def findByIdInCache(id: Int): Option[A] //※్தͰFuture͕ࣦഊ or Option͕NoneͩͬͨΒɺͦ͜Ͱॲཧ͸ऴྃ - 2ॏMonadͷϘΠϥʔϓϨʔτΛݮΒ͢ - ҎԼͷΑ͏ͳྫΛߟ͑Δ

Slide 30

Slide 30 text

OptionT, EitherT .POBE͕ॏʹͳͬͯͨΓɺ
 ҧ͏छྨͩͬͨΓͯ͠΍΍͍͜͠ // Redis͔ΒURLΛऔಘ def getFromRedis(key: String): Future[Option[String]] // ͦͷURLʹHTTPΞΫηεͯ͠IDΛऔಘ def getViaHttp(url: String): Future[Int] // ͦͷIDͰAΛݟ͚ͭΔ def findByIdInCache(id: Int): Option[A] //※్தͰFuture͕ࣦഊ or Option͕NoneͩͬͨΒɺͦ͜Ͱॲཧ͸ऴྃ

Slide 31

Slide 31 text

// Redis͔ΒURLΛऔಘ def getFromRedis(key: String): Future[Option[String]] // ͦͷURLʹHTTPΞΫηεͯ͠IDΛऔಘ def getViaHttp(url: String): Future[Int] // ͦͷIDͰAΛݟ͚ͭΔ def findByIdInCache(id: Int): Option[A] for { url <- getFromRedis(“key”) id <- getViaHttp(url) a <- findByIdInCache(id) } yield a .POBEͷछྨ͕ҧ͏ͷͰ
 GPSࣜͰճ͢͜ͱ΋Ͱ͖ͳ͍ OptionT, EitherT

Slide 32

Slide 32 text

ͦΕ͕Ͱ͖ΔΜͰ͢ʂ … ͦ͏ɺOptionTͳΒͶ

Slide 33

Slide 33 text

import cats.data.OptionT def getFromRedis(key: String): Future[Option[String]] def getViaHttp(url: String): Future[Int] def findByIdInCache(id: Int): Option[A] val result: OptionT[Future, A] = for { url: String <- OptionT(getFromRedis(“key”)) id : Int <- OptionT.liftF(getViaHttp(url)) a : A <- OptionT.fromOption(findByIdInCache(id)) } yield a result.value // Future[Option[A]]

Slide 34

Slide 34 text

import cats.data.OptionT def getFromRedis(key: String): Future[Option[String]] def getViaHttp(url: String): Future[Int] def findByIdInCache(id: Int): Option[A] val result: OptionT[Future, A] = for { url: String <- OptionT(getFromRedis(“key”)) id : Int <- OptionT.liftF(getViaHttp(url)) a : A <- OptionT.fromOption(findByIdInCache(id)) } yield a result.value // Future[Option[A]] 0QUJPO5ͷؔ਺Λ࢖ͬͯɺ
 શ෦0QUJPO5<'VUVSF ">ܕʹม׵

Slide 35

Slide 35 text

import cats.data.OptionT def getFromRedis(key: String): Future[Option[String]] def getViaHttp(url: String): Future[Int] def findByIdInCache(id: Int): Option[A] val result: OptionT[Future, A] = for { url: String <- OptionT(getFromRedis(“key”)) id : Int <- OptionT.liftF(getViaHttp(url)) a : A <- OptionT.fromOption(findByIdInCache(id)) } yield a result.value // Future[Option[A]] ࠷ޙʹWBMVFؔ਺Ͱ
 'VUVSF<0QUJPO<">ʹ໭͢

Slide 36

Slide 36 text

import cats.data.EitherT def getFromRedis(key: String): Future[Either[Error, String]] def getViaHttp(url: String): Future[Int] def findByIdInCache(id: Int): Either[Error, A] val result: EitherT[Future, Error, A] = for { url: String <- EitherT(getFromRedis(“key”)) id : Int <- EitherT.liftF(getViaHttp(url)) a : A <- EitherT.fromEither(findByIdInCache(id)) } yield a result.value // Future[Either[Error, A]] EitherT ͱ͍͏ͷ΋͋Γ·͢

Slide 37

Slide 37 text

࠷ޙʹ - ʮ͜ͷఔ౓ͷཧղͰcatsʹखΛग़͢ͳΜͯ…ʯ
 ͱ͍͏ҙݟ΋͋Δͱࢥ͍·͢ - ·ͣ͸࢖ͬͯΈΔ͜ͱ΋େࣄʢʁʣ - ࢖ͬͯΔ಺ʹͩΜͩΜཧղͯ͘͠Δ

Slide 38

Slide 38 text

͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ

Slide 39

Slide 39 text

No content