Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Upcoming SlideShare
What to Upload to SlideShare
Next
Download to read offline and view in fullscreen.

8

Share

Download to read offline

Real world cats

Download to read offline

[스칼라 나이트](https://festa.io/events/18) 발표자료
소스코드 : https://github.com/ikhoon/real-world-cats

cats 이거 언제 사용해야 해요? cats는 많이 알고 있는 map, flatMap뿐만 아니라 combine, traverse, parMapN, filterA등 다양한 함수를 제공하고 있어 복잡한 엔터프라이즈 로직을 cats가 함수로 간단하게 표현이 가능합니다.

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Real world cats

  1. 1. Real World Cats @ikhoon 1
  2. 2. ! - , (~ 2015) ! - ? (2016) Category theory ! - ... ! (2017) Pure functional ! - (2018 ~) ... 2
  3. 3. , y = f(x) 1. ? 3
  4. 4. , y = f(x) 1. ? : 3, 4 " ." 29 29 https://ko.wikipedia.org/wiki/ %ED%95%A8%EC%88%98#%EB%B6%80%EB%B6%84_%EC%A0%95%EC%9D% 98_%ED%95%A8%EC%88%98_%C2%B7_%EB%8B%A4%EA%B0%80_%ED%95 %A8%EC%88%98 4
  5. 5. , y = f(x) ? 2. ? 5
  6. 6. ... 6
  7. 7. def factorial(n: Int): Either[String, BigInt] = if (n < 0) Left("n 0 ") else if (n == 0) Right(1) else factorial(n - 1).map(_ * n) cats . Either Monadic cats ;-) 7
  8. 8. + 8
  9. 9. 2 — They’re easier to reason about — They’re easier to combine — They’re easier to test — They’re easier to debug — They’re easier to parallelize — They are idempotent — They offer referential transparency — They are memoizable — They can be lazy 2 Book, Functional Programming Simplified - Alvin Alexander 9
  10. 10. — . (compose), (parallel) (memoize), (lazy) . — DRY(Don't Repeat Yourself) 10
  11. 11. ? 1 - ? — — Scala with Cats15 — , "scala cats" 458,000 15 https://underscore.io/training/courses/advanced-scala/ 11
  12. 12. ? 2 - ? — 209 — 3452 — Pull Request 1524 (42 Open, 1482 Closed) — 1.0.0 . 12
  13. 13. ? 3 - ecosystem ? — circe: pure functional JSON library — doobie: a pure functional JDBC layer for Scala — finch: Scala combinator library for building Finagle HTTP services — FS2: compositional, streaming I/O library — h!p4s: A minimal, idiomatic Scala interface for HTTP — Monix: high-performance library for composing asynchronous and event- based programs 31 . 13
  14. 14. Cats - . 14
  15. 15. Cats — Typeclass .17 — Datatype ' A' typeclass instance ' A' typeclass . — ex) Option(datatype) Monad(typeclass) (instance) (pure, flatMap) . 17 http://learnyouahaskell.com/types-and-typeclasses 15
  16. 16. Cats — Type classes 30 — Data types — Instance 30 https://github.com/tpolecat/cats-infographic 16
  17. 17. Cats — Type classes — Data types — Instance . ! Core . . 17
  18. 18. Cats — Type classes — Data types — Instance 18
  19. 19. Cats — Type classes — Data types — Instance cats . OptionT EitherT !! . 19
  20. 20. Cats — Type classes — Data types — Instance Instance Datatype typeclass . Data type type class . . core datatype core typeclass instance .18 18 https://typelevel.org/cats/typeclasses.html#incomplete-type-class-instances-in-cats 20
  21. 21. Cats - Monad ? Monad . Monad ? 21
  22. 22. Monad 8 C . C -> C , End(C) . End(C) , End(C) . End(C) C . 8 https://ko.wikipedia.org/wiki/ %EB%AA%A8%EB%82%98%EB%93%9C_(%EB%B2%94% EC%A3%BC%EB%A1%A0) 22
  23. 23. Cats - Monad ? Monad . ? flatMap(Monad) . 23
  24. 24. Cats - Ordered compare 4 (<, <=, >, >=) . case class Version(major: Int, minor: Int, patch: Int) extends Ordered[Version] { def compare(that: Version): Int = if(major > that.major) 1 else if (major == that.major && minor > that.minor) 1 ... else -1 } Version(1, 11, 1) < Version(1, 1, 1) // false Version(1, 10, 1) > Version(0, 0, 1) // true Version(10, 9, 3) <= Version(0, 0, 1) // false 24
  25. 25. Monad . pure flatMap ! .( 110 ) — map = pure + flatMap — ap = flatMap + map — product = map + ap — map2 = map + product 25
  26. 26. Monad . def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(f andThen pure) def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] = flatMap(fa)(a => map(ff)(f => f(a))) def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] = ap(fb)(map(fa)(a => (b: B) => (a, b))) def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z] = map(product(fa, fb))(f.tupled) 26
  27. 27. cats ... ... ! " backup slide " Cats 27
  28. 28. ! ? 1. 2. 3. cache 4. nullable 28
  29. 29. 1. ! 100 . REST API API 100 API ... GET /v1/api/items/:id API 100 . val itemIds: List[Int] // API def findItemById(itemId: Int): Future[Option[Item]] 29
  30. 30. API Problem API 100ms ? 100 10 ?! . ! API ... 2 . ! ! 30
  31. 31. Solution 25 100 . 100 DDOS . . 100 10 10 . 1 . ! 25 Reactive Microservices Architecture By Jonas Bonér, Co-Founder & CTO Lightbend, Inc. 31
  32. 32. flatMap . traverse (map) (reduce) . flatMap traverse . def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] 32
  33. 33. Future, Lazy, Parallel traverse — Traverse Traverse typeclass . travese . — Parallel (parTraverse, parMapN) cats typeclass . Scala Future Parallel instance .28 — Scala Future (Eager) Eager evalution traverse . — Monix Task (Lazy) Lazy evaluation traverse . monix Task parTraverse . Monix Task . def findItemById(itemId: Int): Task[Item] // Monix Task itemIds.traverse(findItemById) // 10 itemIds.parTraverse(findItemById) // 1 28 https://www.reddit.com/r/scala/comments/7qimqg/cats_parallel_future_traverse/ 33
  34. 34. 10 , 10 API - grouped : 10 - foldLeft : List - flatMap : 10 10 - traverse : 10 .( parTraverse) ! val items = itemIds .grouped(10).toList // .foldLeft(List.empty[Item].pure[Future]) ((acc, group) => acc.flatMap(xs => // group.traverse(findItemById).map(xs ::: _))) // 34
  35. 35. 1. 2. 35
  36. 36. 2. Problem ID ID . . val itemIds: List[Int] // API def isActiveItem[F[_]](itemId: Int)(implicit F: Effect[F]): F[Boolean] filter + async 36
  37. 37. 2. ( ) . // 1. map . val step1: List[IO[(Int, Boolean)]] = itemIds.map(itemId => isActiveItem[IO](itemId).tupleLeft(itemId)) // 2. IO List val step2: IO[List[(Int, Boolean)]] = step1.sequence // 3. filter . val step3: IO[List[(Int, Boolean)]] = step2.map(xs => xs.filter(_._2)) // 4. Boolean val step4: IO[List[Int]] = step3.map(_.map(_._1)) 37
  38. 38. . ! . ? 38
  39. 39. . ! . ? ! !!! filterA . 39
  40. 40. filterA . . filterA . import cats.mtl.implicits._ // filterA . val activeItem: IO[List[Int]] = itemIds.filterA(isActiveItem[IO]) 4 step filterA . . ! FP !!! . 40
  41. 41. filterA def filterA[G[_], A] (fa: F[A])(f: A => G[Boolean]) (implicit G: Applicative[G]): G[F[A]] — filterA predicate A => G[Boolean] . — G Applicative Applicative instance List, Future, Option . — filterA . f: A => Option[Boolean] . 41
  42. 42. cats 1.x filterA cats 0.x filterA import import cats.implicits._ // 0.x cats 1.x filterA cats-mtl23 . import . libraryDependencies += "org.typelevel" %% "cats-mtl" % "0.2.1" import cats.mtl.implicits._ // import 23 https://github.com/typelevel/cats-mtl/blob/f3bd9aa4c8c0466d8cb4aa25bfcc6480531929c5/ core/src/main/scala/cats/mtl/TraverseEmpty.scala#L48 42
  43. 43. 1. 2. 3. cache 43
  44. 44. 3. cache ! cache . Cache miss backup . Problem - . . . . cache layer . def fetch[F[_]](id: Int)(implicit F: Effect[F]): F[Option[User]] = fetchLocalCache(id).flatMap { //local cache case cached @ Some(_) => F.pure(cached) // case None => // fetchRemoteCache(id).flatMap { //remote cache case cached @ Some(_) => F.pure(cached)// . case None => fetchDB(id) // DB . } } 44
  45. 45. Problem - . def fetch[F[_]](id: Int)(implicit F: Effect[F]): F[Option[User]] = fetchLocalCache(id).flatMap { // local cache case cached @ Some(_) => F.pure(cached) // case None => // fetchRemoteCache(id).flatMap { // remote cache case cached @ Some(_) => F.pure(cached) // . case None => fetchDB(id) // DB . } } 45
  46. 46. Cache miss cache layer (pattern) ? 46
  47. 47. Cache miss cache layer (pattern) ? ! !!! SemigroupK “pick the first winner” 10 . 10 Book, Functional Programming for Mortals with Scalaz - Sam Halliday 47
  48. 48. SemigroupK - . F[A] @typeclass trait SemigroupK[F[_]] { @op("<+>") def combineK[A](x: F[A], y: F[A]): F[A] } - Plus (or as cats have renamed it, SemigroupK) has "pick the first winner, ignore errors" semantics. Although it is not expressed in its laws.11 11 https://github.com/typelevel/cats-effect/issues/94#issuecomment-351736393 48
  49. 49. SemigroupK - IO IO . . // "First" . IO.apply lazy . val res1 = IO { "First" } <+> IO { "Second" } res1.unsafeRunSync() // res1: "First" // "First Error" ! "Second" . val res2 = IO.raiseError(new Exception("First Error")) <+> IO { "Second" } res2.unsafeRunSync() // res2: "Second" 49
  50. 50. SemigroupK - Option Option || . . Some . 1.some <+> none // Some(1) ! 1.some <+> 2.some // Some(1) ! none <+> 2.some // Some(2) " 50
  51. 51. SemigroupK Cache miss def fetch[F[_]](id: Int)(implicit F: Effect[F]): F[Option[User]] = fetchLocalCache(id).flatMap { //local cache case cached @ Some(_) => F.pure(cached) // case None => // fetchRemoteCache(id).flatMap { //remote cache case cached @ Some(_) => F.pure(cached)// . case None => fetchDB(id) // DB . } } 51
  52. 52. SemigroupK Cache miss . ! FP !!! def fetch[F[_]: Effect](id: Int): F[Option[User]] = ( OptionT(fetchLocalCache(id)) <+> OptionT(fetchRemoteCache(id)) <+> OptionT(fetchDB(id)) ).value cache layer cache layer . 52
  53. 53. 1. 2. 3. cache 4. nullable 53
  54. 54. 4. nullable Problem API null Future[Option[A]] . — DB select one — Cache get — Http request Future Option ... monadic .. ? 54
  55. 55. 4. Future[Option[A]] order -> user -> address . Future[Option[A]] for . for { orderOption <- findOrder(orderId) userOption <- orderOption match { case Some(order) => findUser(order.userId) case None => Future.successful(None) } address <- userOption match { case Some(user) => findAddress(user.id) case None => Future.successful(None) } } yield address 55
  56. 56. 4. Monad Transformer Save Us Monad Transformer . . ! cats Monad Transformer datatype . 56
  57. 57. github ... Monad transformer ... 19 Monad Transformer ... Monad Transformer . 19 https://github.com/ikhoon/scala-note/blob/master/scala-exercise/src/test/scala/catsnote/ MonadTransformerSaveUs.scala 57
  58. 58. Monad Transformer ? Monad . Monad flatMap . ! Monad Tranformer Monad . Monad Scala with Cats15 5 . ! 15 https://underscore.io/training/courses/advanced- scala/ 58
  59. 59. Monad Transformer WriterT, StateT Monad Transformer OptionT EitherT .(flatMap ) OptionT = monad tranformer for Option 5 OptionT[F[_], A] is a light wrapper on an F[Option[A]] EitherT = monad tranformer for Either 6 EitherT[F[_], A, B] is a light wrapper for F[Either[A, B]] 6 https://typelevel.org/cats/datatypes/eithert.html 5 https://typelevel.org/cats/datatypes/optiont.html 59
  60. 60. - OptionT flatMap A => F[Option[A]] Option[A] => F[Option[B]] . case class OptionT[F[_], A](value: F[Option[A]]) { def flatMapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): OptionT[F, B] = { /* : F.flatMap Option[A] => F[Option[B]] . f A => F[Option[B] . : Option fn f Option[A] => F[Option[B]] . */ def fn(fa: Option[A]): F[Option[B]] = fa match { case Some(v) => f(v) case None => F.pure[Option[B]](None) } OptionT(F.flatMap(value)(fn)) } def flatMap[B](f: A => OptionT[F, B])(implicit F: Monad[F]): OptionT[F, B] = flatMapF(a => f(a).value) } 60
  61. 61. - F[Option[A]] OptionT OptinonT for { orderOption <- findOrder(orderId) userOption <- orderOption match { case Some(order) => findUser(order.userId) case None => Future.successful(None) } addr <- userOption match { case Some(user) => findAddress(user.id) case None => Future.successful(None) } } yield addr 61
  62. 62. - F[Option[A]] OptionT . ! FP !!! val address = for { order <- OptionT(findOrder(orderId)) user <- OptionT(findUser(order.userId)) addr <- OptionT(findAddress(user.id)) } yield addr 62
  63. 63. - Future[Option[A]] A, Option[A], Future[A], Future[Option[A]] real world . . ! . lift OptionT . def getUserDevice(userId: String): Future[Option[Device]] = { val userId = parseUserId(userId) // Option[Long] val user = findUser(userId) // Future[Option[User]] val phoneNumber = getPhoneNumber(user) // String val device = findDevice(phoneNumber) // Future[Device] device } 63
  64. 64. - OptionT - A => Future[Option[B]] ? OptionT ! - A => Option[C] ? OptionT.fromOption[Future] . - A => D ? OptionT.pure[Future, A] ! - A => Future[E] ? OptionT.liftF . - Future[Option[A]] !!!! def findDeviceByUserId(userId: String): Future[Option[Device]] = (for { userId <- OptionT.fromOption[Future](parseUserId(userId)) // Option[Long] user <- OptionT(findUser(userId)) // Future[Option[User]] phone <- OptionT.pure[Future](getPhoneNumber(user)) // String device <- OptionT.liftF(findDevice(phone)) // Future[Device] } yield device).value 64
  65. 65. . !" Learning resources — https://typelevel.org/cats/ , — https://www.scala-exercises.org/cats — http://eed3si9n.com/herding-cats — https://underscore.io/training/courses/advanced-scala/ — ( ) # 65
  66. 66. BACKUP SLIDE 66
  67. 67. - Typeclass API + 67
  68. 68. Monoid 7 ( : monoid) , . , . 7 https://ko.wikipedia.org/wiki/ %EB%AA%A8%EB%85%B8%EC%9D%B4%EB%93%9C 68
  69. 69. API map, flatMap Monad instance monadic Option, List, Future datatype map flatMap . pure, handleError . - monad (pure) - (handleError) API . . 69
  70. 70. API data type scala future successful failed recover twitter future value exception handle monix task pure onErrorRecover raiseError . . API . ! ! ! 70
  71. 71. API 71
  72. 72. 72
  73. 73. API 73
  74. 74. cats typeclass — scala.cocurent.Future — com.twitter.util.Future — monix.eval.Task — cats.effect.IO API . 74
  75. 75. , scala Future API . def getItem(id: Long): ScalaFuture[Either[Throwable, ItemDto]] = if(id < 0) ScalaFuture.successful( Left(new IllegalArgumentException(s" ID . $id"))) else itemRepository.findById(id) .flatMap(item => optionRepository.findByItemId(item.id) .map(option => Right(ItemDto(item.id, item.name, option)) .recover { case ex: Throwable => Left(ex)} 75
  76. 76. cats API def getItem[F[_]](id: Long) (implicit F: MonadError[F, Throwable]): F[Either[Throwable, ItemDto]] = if(id < 0) F.pure( Left(new IllegalArgumentException(s" ID . $id"))) else itemRepository[F].findById(id) .flatMap(item => optionRepository[F].findByItemId(item.id) .map(option => Either.right[Throwable, ItemDto](ItemDto(item.id, item.name, option)))) .handleError(ex => Left(ex)) pure, flatMap, map, handleError cats api . 76
  77. 77. Effect MonadError instance monadic data type . // before - only scala future def getItem(id: Long): ScalaFuture[Either[Throwable, ItemDto]] // after def getItem[F[_]] (id: Long)(implicit F: MonadError[F, Throwable]) : F[Either[Throwable, ItemDto]] // getItem . // ScalaFuture, TwitterFuture MonixTask cats api . val scalaFuture = getItem[ScalaFuture](10) val twiiterFuture = getItem[TwitterFuture](10) val monixTask = getItem[MonixTask](10) 77
  78. 78. API CompletableFuture ... // java completable future val a = CompletableFuture.completedFuture(10) // map val b: CompletableFuture[Int] = a.thenApply(x => x + 10) // flatMap val c = b.thenCompose(x => CompletableFuture.completedFuture(x * 10)) 78
  79. 79. . thenApply, thenCompose . 79
  80. 80. - Monad instance // CompletableFuture instance . implicit val futureInstance = new Monad[CompletableFuture] with StackSafeMonad[CompletableFuture] { def pure[A](x: A): CompletableFuture[A] = CompletableFuture.completedFuture(x) def flatMap[A, B] (fa: CompletableFuture[A]) (f: A => CompletableFuture[B]): CompletableFuture[B] = fa.thenCompose(f.asJava) } 80
  81. 81. Native API vs Cats API . // Java completable future val a = CompletableFuture.completedFuture(10) val b = a.thenApply(x => x + 10) val c = b.thenCompose(x => CompletableFuture.completedFuture(x * 10)) // Cats monad api val a2 = 10.pure[CompletableFuture] val b2 = a2.map(x => x + 10) val c2 = b2.flatMap(x => (x * 10).pure[CompletableFuture]) 81
  82. 82. Real World Cats . Future, HashMap, HttpServer . API . Cats API . 82
  83. 83. (2015) ...12 12 https://github.com/typelevel/cats/issues/95#issuecomment-114023955 83
  84. 84. Cats - Typeclass . ! Functor Monad . trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] } trait Monad[F[_]] { def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] } 84
  85. 85. Cats - Data types . ! OptionT EitherT . — OptionT = monad tranformer for Option 5 OptionT[F[_], A] is a light wrapper on an F[Option[A]] — EitherT = monad tranformer for Either 6 EitherT[F[_], A, B] is a lightweight wrapper for F[Either[A, B]] 6 https://typelevel.org/cats/datatypes/eithert.html 5 https://typelevel.org/cats/datatypes/optiont.html 85
  86. 86. Cats - Instance scala stdlib data type cats typeclass instance // .../cats/instances/all.scala trait AllInstances xtends AnyValInstances with ... with ListInstances with MapInstances with OptionInstances with OrderInstances with OrderingInstances with TupleInstances with UUIDInstances with VectorInstances with ... cats data type instance 86
  87. 87. Do It Yourself ! monad transformer cats ! 2016 9 25 monad transformer ...20 20 http://loicdescotte.github.io/posts/scala-compose- option-future/ 87
  88. 88. Monad Transformer — . — lift . — Future[Option[A]] OptionT . 88
  89. 89. 3. - Monad Tranformer ? Monad compose Monad . Applicative .22 trait Applicative[F[_]] { def compose[G[_]: Applicative]: Applicative[λ[α => F[G[α]]]] = new ComposedApplicative[F, G] { val F = self val G = Applicative[G] } } Monad[Future].compose[Option] // Applicative[Future[Option[α]]] 22 https://github.com/typelevel/cats/blob/2ae785f726b810438fa5b429d5c9d28f1be2e69d/core/ src/main/scala/cats/Applicative.scala#L88-L92 89
  90. 90. 3. - Monad Tranformer ? Future[Option[A]] Monad Monad Transformer .21 21 http://book.realworldhaskell.org/read/monad-transformers.html 90
  91. 91. Cats Monad 50 XXX2~22 60 110 . // scala reflection import scala.reflect.runtime.universe._ typeOf[Monad[Future]].members.map(show(_)) // widen, whileM_, whileM, whenA, void, untilM_, untilM, unlessA, unit, tupleRight, tupleLeft, replicateA, product, productREval, productR, productLEval, productL, point, mproduct, map, lift, iterateWhileM, iterateWhile, iterateUntilM, iterateUntil, imap, ifM, fproduct, forEffect, forEffectEval,followedBy, followedByEval, fmap, flatten, flatTap, compose, composeFunctor, composeContravariant, composeContravariantMonoidal, composeApply, as, ap, <*, <*>, *>, tuple2~22, map2Eval, map2~22, ap2~22 91
  92. 92. cache miss import cats.effect.Effect // local cache def fetchLocalCache[F[_]] (key: Int)(implicit F: Effect[F]): F[Option[User]] // remote cache def fetchRemoteCache[F[_]](key: Int)(implicit F: Effect[F]): F[Option[User]] // persistence db def fetchDB[F[_]] (key: Int)(implicit F: Effect[F]): F[Option[User]] 92
  93. 93. 93
  • JuHwanHong

    Jul. 8, 2021
  • webtk

    Oct. 31, 2020
  • sahngjulee

    Sep. 18, 2020
  • danieltedkim

    Nov. 14, 2019
  • JoonsunBaek

    Jun. 28, 2019
  • changwonchoe7

    May. 31, 2018
  • MinchulPark1

    May. 21, 2018
  • ssuser22448f

    Apr. 15, 2018

[스칼라 나이트](https://festa.io/events/18) 발표자료 소스코드 : https://github.com/ikhoon/real-world-cats cats 이거 언제 사용해야 해요? cats는 많이 알고 있는 map, flatMap뿐만 아니라 combine, traverse, parMapN, filterA등 다양한 함수를 제공하고 있어 복잡한 엔터프라이즈 로직을 cats가 함수로 간단하게 표현이 가능합니다.

Views

Total views

3,014

On Slideshare

0

From embeds

0

Number of embeds

1,687

Actions

Downloads

14

Shares

0

Comments

0

Likes

8

×