Professional Documents
Culture Documents
Scalaz 8: Flatmap (Oslo) 2018
Scalaz 8: Flatmap (Oslo) 2018
SCALAZ 8
A WHOLE NEW GAME
flatMap(Oslo) 2018
By John A. De Goes — @jdegoes
2
SCALAZ 8
Dec 6 2008 ... May 10 2011 Jul 24 2012
WHEN IT’S READY!
First Commit Scalaz 6.0 Scalaz 7.0
(SOON)
3
A F E W O F TH E
SCALAZ 8 CONTRIBUTORS
Plus Vincent Marquez, Stephen Compall, Edmund Noble, Kenji Yoshida, Emily Pillmore, Jose Cardona, Dale Wijnand, Harrison Houghton, & others. And John!
4
4 BIG DEALS
Let’s explore how Scalaz 8 is changing the game!
TYPE CLASSES
Semigroup
Monoid
TYPE CLASS HEAVEN
6
trait Semigroup[A] {
def append(l: => A, r: => A): A
}
object Semigroup { TYPE CLASSES
def apply[A](implicit S: Semigroup[A]) = S
}
implicit class SemigroupSyntax[A](l: A) { TYPE CLASS HIERARCHY
def <> (r: => A)(implicit S: Semigroup[A]): A =
S.append(l, r)
} TYPE CLASS ENCODING
trait Monoid[A] extends Semigroup[A] {
def zero: A
} TYPE CLASS HEAVEN
object Monoid {
def apply[A](implicit S: Monoid[A]) = S
}
7
TYPE CLASSES
Functor
Traversable Monad
TYPE CLASS HELL
9
trait Functor[F[_]] {
... TYPE CLASSES
}
trait Monad[F[_]] extends Functor[F] {
TYPE CLASS HIERARCHY
...
}
trait Traversable[F[_]] extends TYPE CLASS ENCODING
Functor[F] {
... TYPE CLASS HELL
}
10
TYPE CLASSES
Functor
Traversable Monad
TYPE CLASS HEAVEN
12
trait FunctorClass[F[_]] { … }
trait MonadClass[F[_]] extends FunctorClass[F] { … }
trait TraversableClass[F[_]] extends FunctorClass[F] { … }
trait BH0 extends BH1 {
TYPE CLASSES
implicit def monadFunctor[M[_]](implicit M: Monad[M]):
Functor[M] = instanceOf(M)
}
TYPE CLASS HIERARCHY
trait BH1 {
implicit def traversableFunctor[T[_]](
implicit T: Traversable[T]): Functor[T] = instanceOf(T)
} TYPE CLASS ENCODING
trait BaseTypeclasses {
type Functor[F[_]] = InstanceOf[FunctorClass[F]]
type Monad[M[_]] = InstanceOf[MonadClass[M]]
TYPE CLASS HEAVEN
type Traversable[T[_]] = InstanceOf[TraversableClass[T]]
}
trait Scalaz extends BH0 with BaseTypeclasses
14
implicit val ListTraversable: Traversable[List] =
instanceOf(new TraversableClass[List] {
...
})
TYPE CLASSES
implicit val ListMonad: Monad[List] =
instanceOf(new MonadClass[List] {
... TYPE CLASS HIERARCHY
})
// YAY!!!!
doStuff(1 :: 2 :: 3 :: Nil)
15
4 BIG DEALS
Let’s explore how Scalaz 8 is changing the game!
// OK
CATEGORY THEORY
trait Functor[F[_]] extends Invariant[F] {
def map[A, B](fa: F[A])(f: A => B): F[B]
} “FUNCTOR”
// Better
FUNCTOR
trait FunctorClass[F[_]] {
def map[A, B](f: A => B): F[A] => F[B]
}
OPPORTUNITY COST
17
CATEGORY THEORY
“FUNCTOR”
FUNCTOR
OPPORTUNITY COST
18
val charP =
char ^ subset(c => c >= 32 && c != '"' && c != '\\') |
(text("\\") *> escapedP)
val strP
val jStrP
= (text("\"") *> charP.many <* text("\"")) ^ chars
= strP ^ str_ ^ fix
CATEGORY THEORY
val digitP = (char ^ subset(c => c >= '0' &&
c <= '9')).label("digit")
val nonZeroP = (char ^ subset(c => c >= '1' && “FUNCTOR”
c <= '9')).label("non-zero digit")
val numP = (pure(1) | text("-") ^ element(-1)) *
(text("0") ^ element("0") |
FUNCTOR
(nonZeroP * digitP.many) ^ cons ^ chars) *
(text(".") *> digitP.many1 ^ chars).optional *
((text("e") | text("E")) *>
(pure(1) |
OPPORTUNITY COST
text("+") ^ element(1) |
text("-") ^ element(-1)) *
(digitP.many1 ^ chars)).optional
20
scalaz-http
CATEGORY THEORY
scalaz-codec
FUNCTOR
scalaz-parsers
ENDOFUNCTOR IN SCALA
scalaz-rpc
OPPORTUNITY GAIN
scalaz-analytics
...
23
4 BIG DEALS
Let’s explore how Scalaz 8 is changing the game!
object Just {
}
def unapply[A](ma: Maybe[A]): Option[A] = toOption(ma)
OPAQUE TYPES
object Empty {
def unapply[A](ma: Maybe[A]): Boolean = toOption(ma).isEmpty
} INTRODUCTION - MAYBE
}
def instanceOf[T](t: T): InstanceOf[T]
OPAQUE TYPES
object InstanceOfModule {
val impl: InstanceOfModule = new InstanceOfModule {
INTRODUCTION - MAYBE
override type InstanceOf[T] = T
override def instanceOf[T](t: T) = t TYPE CLASS HIERARCHY
}
} FIX - RECURSION SCHEMES
VOID
@inline
final def instanceOf[T](t: T): InstanceOf[T] =
InstanceOfModule.impl.instanceOf(t)
26
trait FixModule {
type Fix[F[_]]
OPAQUE TYPES
def fix[F[_]](f: F[data.Fix[F]]): Fix[F]
def unfix[F[_]](f: Fix[F]): F[data.Fix[F]]
} INTRODUCTION - MAYBE
trait IListModule {
type IList[A]
trait VoidModule {
type Void
OPAQUE TYPES
def absurd[A](v: Void): A
}
INTRODUCTION - MAYBE
4 BIG DEALS
Let’s explore how Scalaz 8 is changing the game!
// Program #1
val f1 = Future(getProductCategories())
val f2 = Future(getSponsoredProducts())
EFFECTS
FUTURE
for {
categories <- f1 IO[E, A]
sponsored <- f2
response <- buildResults(categories, sponsored) ERROR HANDLING
} yield response
RESOURCE SAFETY
≠ CONCURRENCY
// Program #2 INTERRUPTION
for {
categories <- Future(getProductCategories()) IOREF[A]
sponsored <- Future(getSponsoredProducts())
response <- buildResults(categories, sponsored) IOQUEUE[A]
} yield response
EXAMPLE
31
// Program #1
val f1 = Future(getProductCategories())
val f2 = Future(getSponsoredProducts())
EFFECTS
FUTURE
for {
categories <- f1 IO[E, A]
sponsored <- f2
response <- buildResults(categories, sponsored) ERROR HANDLING
} yield response
RESOURCE SAFETY
≠ CONCURRENCY
// Program #2 INTERRUPTION
for {
categories <- Future(getProductCategories()) IOREF[A]
sponsored <- Future(getSponsoredProducts())
response <- buildResults(categories, sponsored) IOQUEUE[A]
} yield response
EXAMPLE
32
EFFECTS
FUTURE
IO[E, A]
class Future[+T] {
...
ERROR HANDLING
def flatMap[S](f: (T) ⇒ Future[S])(implicit
RESOURCE SAFETY
executor: ExecutionContext): Future[S] = ???
... CONCURRENCY
}
INTERRUPTION
IOREF[A]
IOQUEUE[A]
EFFECTS
FUTURE
IO[E, A]
class Future[+T] {
...
ERROR HANDLING
def flatMap[S](f: (T) ⇒ Future[S])(implicit
RESOURCE SAFETY
executor: ExecutionContext): Future[S] = ???
... CONCURRENCY
}
INTERRUPTION
IOREF[A]
IOQUEUE[A]
EFFECTS
FUTURE
ERROR HANDLING
Future
RESOURCE SAFETY
CONCURRENCY
http://github.com/scalaz/scalaz
INTERRUPTION
IOREF[A]
115x FASTER!
IOQUEUE[A]
EXAMPLE
35
EFFECTS
FUTURE
ERROR HANDLING
Future
RESOURCE SAFETY
CONCURRENCY
http://github.com/scalaz/scalaz
INTERRUPTION
IOREF[A]
115x FASTER!
IOQUEUE[A]
EXAMPLE
36
EFFECTS
IO[E, A] FUTURE
IO[E, A]
IOREF[A]
or synchronously /
asynchronously IOQUEUE[A]
compute a value of
type A.
EXAMPLE
37
io.map(f: A => B): IO[E, B] Maps one value into another by IOREF[A]
applying the function on A.
EFFECTS
try { FUTURE
try {
IO[E, A]
try throw new Exception("e1")
finally throw new Exception("e2") ERROR HANDLING
} finally throw new Exception("e3")
RESOURCE SAFETY
} catch {
// WHICH ONE??? CONCURRENCY
case e : Exception => println(e.toString())
INTERRUPTION
}
IOREF[A]
EXAMPLE
39
Error State: E Defect: Throwable
Recoverable Error Non-Recoverable Error
EFFECTS
FUTURE
IO[E, A]
Unhandled E Interruption
ERROR HANDLING
RESOURCE SAFETY
CONCURRENCY
IOREF[A]
Recover with attempt :
IO[E, A] => IO[Void, E \/ A] IOQUEUE[A]
EXAMPLE
Terminate fiber
(“Let it Crash”)
40
EFFECTS
FUTURE
IO[E, A]
val result : IO[Void, String \/ A]
= IO.fail(“e1”).ensuring( ERROR HANDLING
IO.terminate(new Error(“e2”)).ensuring(
RESOURCE SAFETY
IO.terminate(new Error(“e3)).attempt
CONCURRENCY
result.flatMap(putStrLn(_)) // ???
INTERRUPTION
IOREF[A]
IOQUEUE[A]
EXAMPLE
41
EFFECTS
try { FUTURE
IOQUEUE[A]
EXAMPLE
43
IO[E, A]
Use openFile(f1).bracket(_.close()) {
ERROR HANDLING
file1 =>
RESOURCE SAFETY
openFile(f2).bracket(_.close()) {
CONCURRENCY
file2 =>
joinFile(file1, file2) INTERRUPTION
} IOREF[A]
}
IOQUEUE[A]
EXAMPLE
44
EFFECTS
// Fork/join: FUTURE
def concurrentFib(n: Int): IO[Void, BigInt] =
IO[E, A]
if (n <= 1) IO.point[Void, BigInt](n)
else ERROR HANDLING
for {
RESOURCE SAFETY
f1 <- concurrentFib(n - 1).fork
f2 <- concurrentFib(n - 2).fork CONCURRENCY
v1 <- f1.join
INTERRUPTION
v2 <- f2.join
IOREF[A]
} yield v1 + v2
IOQUEUE[A]
EXAMPLE
45
EFFECTS
FUTURE
IO[E, A]
ioA.par(ioB) CONCURRENCY
INTERRUPTION
IOREF[A]
IOQUEUE[A]
EXAMPLE
46
EFFECTS
FUTURE
// Race 2 or more actions: IO[E, A]
getUrl(“backup”)) CONCURRENCY
INTERRUPTION
IOREF[A]
IOQUEUE[A]
EXAMPLE
47
object cats {
def fib(n: Int): IO[BigInt] =
if (n <= 1) IO(n)
else
EFFECTS
fib(n - 1).flatMap { a =>
FUTURE
fib(n - 2).flatMap(b => IO(a + b))
}
IO[E, A]
}
object scalaz { ERROR HANDLING
def fib(n: Int): IO[Void, BigInt] =
if (n <= 1) IO.point[Void, BigInt](n) RESOURCE SAFETY
else
fib(n - 1).flatMap { a =>
CONCURRENCY
fib(n - 2).flatMap(b => IO.point(a + b))
INTERRUPTION
}
} IOREF[A]
// Never finishes!!!
cats.fib(Int.MaxValue).start(_.cancel) IOQUEUE[A]
RESOURCE SAFETY
// Computes quickly by
CONCURRENCY
// interrupting loser: INTERRUPTION
scalaz.fib(Int.MaxValue). IOREF[A]
race(scalaz.fib(10)) IOQUEUE[A]
EXAMPLE
49
EFFECTS
FUTURE
IOREF[A]
IOQUEUE[A]
EXAMPLE
50
EFFECTS
FUTURE
// Asynchronous, non-blocking queue:
IO[E, A]
for {
ERROR HANDLING
queue <- IOQueue.make[Int]
fiber <- queue.take.fork RESOURCE SAFETY
IOQUEUE[A]
EXAMPLE
type Actor[E, I, O] = I => IO[E, O] 51
implicit class ActorSyntax[E, I, O](actor: Actor[E, I, O]) {
def ! (i: I): IO[E, O] = actor(i)
}
EFFECTS
val makeActor: IO[Void, Actor[Void, Int, Int]] =
for { FUTURE
counter <- IORef(0)
queue <- IOQueue.make[(Int, Promise[Void, Int])]
IO[E, A]
worker <- queue.take.flatMap(t =>
ERROR HANDLING
counter.modify(_ +
t._1).flatMap(t._2.complete)).forever.fork
RESOURCE SAFETY
actor = (n: Int) =>
for { CONCURRENCY
promise <- Promise.make[Void, Int]
_ <- queue.offer((n, promise)) INTERRUPTION
value <- promise.get
} yield value IOREF[A]
} yield actor
... IOQUEUE[A]
for {
actor <- makeActor EXAMPLE
v <- actor ! 20
} yield v
52
SCALAZ 8 IS
COMING
SOON!
THE END
THANK YOU!
Thanks to the organizers of flatMap, the
sponsors, & attendees.
Follow me @jdegoes
Join Scalaz at gitter.im/scalaz/scalaz