[스칼라 나이트](https://festa.io/events/18) 발표자료
소스코드 : https://github.com/ikhoon/real-world-cats
cats 이거 언제 사용해야 해요? cats는 많이 알고 있는 map, flatMap뿐만 아니라 combine, traverse, parMapN, filterA등 다양한 함수를 제공하고 있어 복잡한 엔터프라이즈 로직을 cats가 함수로 간단하게 표현이 가능합니다.
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
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
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
47. Cache miss
cache layer
(pattern) ?
!
!!!
SemigroupK
“pick the first winner” 10
.
10
Book, Functional Programming for Mortals with Scalaz - Sam Halliday
47
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
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. 4. Monad Transformer Save Us
Monad Transformer .
.
!
cats
Monad Transformer datatype .
56
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. - 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. - 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. - 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. - 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
70. API
data type
scala future successful failed recover
twitter future value exception handle
monix task pure onErrorRecover raiseError
.
.
API .
! ! !
70
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. 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
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. 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
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. 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. Do It Yourself !
monad
transformer
cats
!
2016 9 25
monad transformer
...20
20
http://loicdescotte.github.io/posts/scala-compose-
option-future/
87