Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scalaで関数型 再入門

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Scalaで関数型 再入門

よく遭遇する、Scala標準ライブラリだけだと大変なケースでも、
Catsを使うとうまく関心事を分離して、クリーンかつ拡張に対して頑強になることがあります。

このスライドでは、そういった場合のCatsの使い所について、初学者向けに解説をしています。

2019年7月26日のセプテーニ・オリジナルでの社内勉強会資料です。

Avatar for Taisuke Oe

Taisuke Oe

July 26, 2019
Tweet

More Decks by Taisuke Oe

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ Taisuke Oe / ຑ২ ହี Twitter: @OE_uia GitHub: @taisukeoe

    Septeni Original, Inc. ٕज़ΞυόΠβʔ ScalaMatsuri ࠲௕
  2. Catsͷ࢖͍ํ build.sbt ʹҎԼͷ2ߦΛ௥Ճ Cats 1.6.1͸࠷৽stable൛ͰɺScala 2.11, 2.12ʹରԠɻ Scala 2.13Λ࢖༻͢Δ৔߹͸ɺCats 2.0.0-M4͕࠷৽

    scalacOptions += "-Ypartial-unification" libraryDependencies += "org.typelevel" %% "cats-core" % "1.6.1"
  3. List[Either[Error, Int]] val eitherList: List[Either[Error, Int]] = List(Right(1), Right(2), Right(3))

    eitherList.foldLeft(Right(0):Either[Error, Int]) { case (Right(v1), Right(v2)) => Right(v1 + v2) case (Right(_), l @ Left(_)) => l case (l @ Left(_), _) => l } foldLeft಺Ͱɺཁૉܕͷߏ଄ͷ෼ղ͕ඞཁ ߏ଄͕ਂ͘ωετ͢Δ΄Ͳɺίʔυྔ͕૿͑Δɻ ࠶ར༻͠ʹ͘͘ͳΔɻ
  4. ଍͠ࢉ Either[Error, Int]ܕ ͷ଍͠ࢉʹ͸ɺߏ଄ͷ ෼ղ͕ඞཁ Right(1) + Right(2) / /

    Right(3) ੒ޭ஋Int͕ɺ஋Ϋϥε(ྫ: Price)ʹมΘͬ ͯ΋ɺ࠶ར༻͍ͨ͠
  5. Semigroupͷ࢖͍ํ import cats._ import cats.implicits._ 1 |+| 2 // 3

    Option(1) |+| Option(2) // Some(3) (Right(1): Either[Error, Int]) |+| (Right(2): Either[Error, Int]) // Right(3)
  6. OptionܕΛฦ͢.some import cats._ import cats.implicits._ 1.some |+| 2.some //Some(3) 1.some

    |+| none //Some(1) 1.asRight[Error] |+| 2.asRight[Error] //Right(3)
  7. ஋Ϋϥε Price case class Price(private val value: Int) extends AnyVal

    { def +(p: Price): Price = this.copy(value = value + p.value) } implicit val priceMonoid: Monoid[Price] = new Monoid[Price] { override def empty: Price = Price(0) override def combine(x: Price, y: Price): Price = x + y } import cats._, cats.implicits._ Foldable[List].fold( List(Price(100).asRight[Error], Price(200).asRight[Error]) ) //Price(300)
  8. Լ४උ trait Product case class GoodProduct(id: Long, price: Price) extends

    Product case class NiceProduct(id: Long, price: Price) extends Product trait ProductRepository[F[_], T <: Product] { def resolveById(id: Long): F[T] } val goodRepo = new ProductRepository[Future, GoodProduct]{ override def resolveById(id: Long): Future[GoodProduct] = ??? } val niceRepo = new ProductRepository[Future, NiceProduct]{ override def resolveById(id: Long): Future[NiceProduct] = ??? } def taxing(product: Product): Future[Price] = ???
  9. Futureͷ߹੒: ௚ྻ FutureΠϯελϯεͷੜ੒͕ɺଞͷFutureͷܭࢉ݁Ռʹґଘ ͍ͯ͠Δ৔߹ forࣜͰ؆୯ʹ߹੒Ͱ͖Δɻ val taxedPrice = for {

    product <- goodRepo.resolveById(1L) taxed <- taxing(product) } yield taxed ͋Δ঎඼ͷՁ֨(੫ࠐ)Λܭࢉ
  10. ෳ਺ͷ௚ྻܭࢉΛฒྻʹ ՄೳͳݶΓฒྻʹ߹੒ͯ͠ΈΔ val niceF = niceRepo.resolveById(2L) val both = for

    { good <- goodRepo.resolveById(1L) taxedGoodF = taxing(good) nice <- niceF taxedNice <- taxing(nice) taxedGood <- taxedGoodF } yield taxedGood + taxedNice ෳ਺ͷ঎඼ͷՁ֨(੫ࠐ)Λ߹ܭ͢Δɻ
  11. ෳ਺ͷ௚ྻܭࢉΛฒྻʹ forࣜΛ෼ׂ͢Δͱɺز෼Ϛγɻ val taxedGood = for { product <- goodRepo.resolveById(1L)

    taxed <- taxing(product) } yield taxed val taxedNice = for { product <- niceRepo.resolveById(2L) taxed <- taxing(product) } yield taxed for { goodPrice <- taxedGood nicePrice <- taxedNice } yield goodPrice + nicePrice ෳ਺ͷ঎඼ͷՁ֨(੫ࠐ)Λ߹ܭ͢Δɻ
  12. ฒྻܭࢉΛ߹੒ ApplicativeελΠϧͰ͸ฒྻܭࢉΛ߹੒͠΍͍͢ import cats._ import cats.implicits._ (goodRepo.resolveById(1L).flatMap(taxing), niceRepo.resolveById(2L).flatMap(taxing)).mapN(_ + _)

    ෳ਺ͷ঎඼ͷՁ֨(੫ࠐ)Λ߹ܭ͢Δɻ Applicative[Future].map2( goodRepo.resolveById(1L).flatMap(taxing), niceRepo.resolveById(2L).flatMap(taxing))(_ + _)
  13. ฒྻܭࢉΛ߹੒ ฒྻͯ͠औಘ/ݕূͨ͠ܭࢉΛɺϑΝΫτϦϝ ιουʹ؆୯ʹྲྀ͠ࠐΊΔ import cats._ import cats.implicits._ case class User(name:

    String, age: Int) val nameFuture: Future[String] = ??? val ageFuture: Future[Int] = ??? (nameFuture, ageFuture).mapN(User.apply)
  14. ͜͜ͰRepositoryͷվम ΤϥʔΛEitherܕͰදݱ͢Δ͜ͱʹͳͬͨ Ͳ͏͢Ε͹ܭࢉ݁ՌΛ߹੒Ͱ͖Δʁ type EitherE[T] = Either[Error, T] type FutureEitherE[T]

    = Future[EitherE[T]] val goodERepo = new ProductRepository[FutureEitherE, GoodProduct]{ override def resolveById(id: Long): FutureEitherE[GoodProduct] = ??? } val niceERepo = new ProductRepository[FutureEitherE, NiceProduct]{ override def resolveById(id: Long): FutureEitherE[NiceProduct] = ??? }
  15. EitherT[Future, A, B] type EitherTFuture[T] = EitherT[Future, Error, T] val

    goodETRepo = new ProductRepository[EitherTFuture, GoodProduct] { override def resolveById(id: Long): EitherTFuture[GoodProduct] = ??? } val niceETRepo = new ProductRepository[EitherTFuture, NiceProduct] { override def resolveById(id: Long): EitherTFuture[NiceProduct] = ??? }
  16. EitherT[Future, A, B] Applicative[EitherTFuture].map2( goodETRepo.resolveById(1L).flatMap(taxing), niceETRepo.resolveById(2L).flatMap(taxing))(_ + _) EitherT͸Monadɻ શͯͷMonad͸ApplicativeͰ΋͋ΔͷͰɺEitherT͸ApplicativeͰ

    ΋͋Δɻ Ͱ΋taxingͷγάωνϟ͕ҰகͤͣɺίϯύΠϧͰ͖ͳ͍ def taxingEF(product: Product): EitherTFuture[Price] = ??? EitherTFuture൛ͷtaxing͕ཉ͍͠
  17. EitherT Future൛ ׬੒! val taxingEF = ɹɹ (taxing _).andThen(f =>

    EitherT(f.map(_.asRight[Error]))) Applicative[EitherTFuture].map2( goodETRepo.resolveById(1L).flatMap(taxingEF), niceETRepo.resolveById(2L).flatMap(taxingEF))(_ + _) ࠓճͷtaxingͷΑ͏ͳɺMonadΛฦؔ͢਺Λ ߹੒ɾ֦ு͍ͨ͠
  18. Kleisli Kleisli[F, A, B] ͸ A => F[B] ͷϥούʔ MonadΛੜ੒͢Δؔ਺Λϥοϓ͢Δ͜ͱ͕ଟ͍

    ͜ͷΑ͏ͳؔ਺Λศརʹѻ͏ͨΊͷϝιου͕ଟ ਺ɻ runϝιουͰɺதͷA => F[B]΁ΞΫηεՄೳ F͕MonadͰ͋Ε͹ɺKleisli[F, A, B]΋Monad
  19. Kleisli ߹੒ def taxing(product: Product): Future[Price] = ??? def exchange(jpy:

    Price): Future[PriceInUSD] = ??? // Function1ͷandThenʹΑΔ߹੒͸Ͱ͖ͳ͍ɻίϯύΠϧΤϥʔɻ // (taxing _) andThen exchange val taxingPriceInUSD = Kleisli(taxing) andThen exchange _ val taxingEFinUSD = taxingInUSD.mapF(f => EitherT(f.map(_.asRight[Error]))) Applicative[EitherTFuture].map2( goodETRepo.resolveById(1L).flatMap(taxingEFinUSD.run), niceETRepo.resolveById(2L).flatMap(taxingEFinUSD.run))(_ + _)
  20. ࣗྗͰsequence val sequenced: Either[Error, List[Price]] = list.foldRight(List.empty[Price].asRight[Error]){ (e, acc) =>

    for { value <- e valueList <- acc } yield value :: valueList } ґଘؔ܎ͷͳ͍(ฒྻ)ܭࢉ͕ొ৔͢Δɻ Applicativeͷ೏͍……