rely on both flatMap and map to sequence computations
for comprehensions
traitMonad[F[_]] {
defpure[A](a: A): F[A] // wrap itdefflatMap[A, B](value: F[A])(func: A => F[B]): F[B]
// apply a transformation function to a monadic valuedefmap[A, B](value: F[A])(func: A => B): F[B] =
flatMap(value)(a => pure(func(a)))
// apply a non-monadic function to a monadic value
}
Cool Monads from scalaz
Id
Write functions that combine Monadic & Non-Monadic values
Switch between async / sync for testing
Prod: Future[String]
Test: Id[String]
State
dealing stateful problems, while still keeping everything nice and pure
Composition: fa.map(f).map(g) is fa.map(f.andThen(g))
Identity: Mapping with the identity function is a no-op fa.map(x => x) = fa
Allow lifting pure to effectful function
deflift[A, B](f: A => B): F[A] => F[B] = fa => map(fa)(f)
// Example implementation for Optionimplicitval functorForOption: Functor[Option] = newFunctor[Option] {
defmap[A, B](fa: Option[A])(f: A => B): Option[B] = fa match {
caseNone => NonecaseSome(a) => Some(f(a))
}
}
Applicatives
Applicative extends Functor with an ap and pure method.
Wrap functions in Contexts!
mapN to apply for different arities
avoid making unnecessary claims about dependencies between computations
using |@|
ap unpacks both Monads, and then applies the function to the value:
caseclassFoo(s: Symbol, n: Int)val maybeFoo = for {
s <- maybeComputeS(whatever)
n <- maybeComputeN(whatever)
} yieldFoo(s, n)
val maybeFoo = maybeComputeS(whatever).flatMap(
s => maybeComputeN(whatever).map(n => Foo(s, n))
)
maybeComputeN never depends on s, compiler still thinks so
val maybeFoo = (maybeComputeS(whatever) |@| maybeComputeN(whatever))(Foo(_, _))
val url = newURL(urlStr)
(S3Bucket(url) |@| S3Key(url)) { (bucket, key) =>
if (bucket == store.bucket) {
key.some
} else {
warn(s"can not delete a resource $url from unknown bucket: $bucket")
none
}
}.flatten.toList
Monoids
traitMonoid[A] {
defcombine(x: A, y: A): Adefempty: A
}
Examples of Monoids:
Ints, with the zero being 0 and the operator being +.
Ints, with the zero being 1 and the operator being *.
Lists, with the zero being Nil and the operator being ++.
Strings, with the zero being "" and the operator being +.
Q: give me some A for which I have a monoid
Usage
traitTotalInstances{
implicitdeftotalMonoid(implicit targetCcy: Currency, mc: MoneyContext): Monoid[Total] =
Monoid.instance[Total]({ (a, b) =>
a + b
}, zero)
defzero(implicit targetCcy: Currency, mc: MoneyContext): Total = {
val zeroCurrency = targetCcy.apply(0).toResponse
Total(
total = zeroCurrency,
product = zeroCurrency,
realProduct = zeroCurrency,
duties = None,
insurance = None,
returns = None,
shipping = zeroCurrency,
taxes = None,
discount = None
)
}
}
Kleisli
val f: Int => Task[String] = ???
val fKleisli: Kleisli[Task, Int, String] = ???
Like a monad Transformer for function composition.
--> The advantage is you can do it within the context of a Future / Task, etc.
andThenK does automatic lifting.
Call run to extract again.
import cats.data._
import scala.concurrent.Futureimport scala.concurrent.ExecutionContext.Implicits.global
import cats.instances.future._
objectKleisliTestextendsApp{
val getNumberFromDb: Unit => Future[Int] = _ => Future.successful(2)
val processNumber: Int => Future[Int] = num => Future.successful(num * 2)
val persistToDb: Int => Future[Boolean] = _ => Future.successful(true)
val kleisliCombo: Kleisli[Future, Unit, Boolean] =
Kleisli(getNumberFromDb)
andThen processNumber
andThen persistToDb
val unpacked: Unit => Future[Boolean] = kleisliCombo.run
unpacked().map(println)
}
Algebraic Data Types
A sum type consisting of various subtypes of other sum and product types.
Analyze values of algebraic data with pattern matching.
Product Type
caseclassStudent(enrolled: Boolean, age: Byte)// Students consists of a Boolean AND Byte// 2 * 256 = 258
Sum Type
sealedtraitColorcaseobjectRedextendsColorcaseobjectBlueextendsColorcaseobjectGreenextendsColor// Color can be Red OR Blue OR Green// 1 + 1 + 1 = 3 distinct possible values