SlideShare ist ein Scribd-Unternehmen logo
1 von 79
Downloaden Sie, um offline zu lesen
One Monad to Rule
Them All
Functional JVM Meetup
Prague, Aug 8 2019
John A. De Goes — @jdegoes
Agenda
1. INTRO TO
FUNCTIONAL EFFECTS
2
2. TOUR OF THE
EFFECT ZOO
3. VERTICAL
EFFECT COMPOSITION
4. INTRO TO
EFFECT ROTATION
5. PRACTICE OF
EFFECT ROTATION
1.
INTRO TO FUNCTIONAL
EFFECTS
3
1. Intro to Functional Effects
4
World of Values
Variables
Remove Duplication
In a Single Expression
Functions
Remove Duplication
Across Multple
Expressions
Combinators
Remove Higher-Order
Duplication Across
Expressions
Testing
Test Values for
Equality, Similarity,
Inequality
1. Intro to Functional Effects
5
World of Values
Variables
Remove Duplication
In a Single Expression
Functions
Remove Duplication
Across Multple
Expressions
Combinators
Remove Higher-Order
Duplication Across
Expressions
Testing
Test Values for
Equality, Similarity,
Inequality
1. Intro to Functional Effects
6
World of Values
Variables
Remove Duplication
In a Single Expression
Functions
Remove Duplication
Across Multple
Expressions
Combinators
Remove Higher-Order
Duplication Across
Expressions
Testing
Test Values for
Equality, Similarity,
Inequality
1. Intro to Functional Effects
7
World of Values
Variables
Remove Duplication
In a Single Expression
Functions
Remove Duplication
Across Multple
Expressions
Combinators
Remove Higher-Order
Duplication Across
Expressions
Testing
Test Values for
Equality, Similarity,
Inequality
1. Intro to Functional Effects
8
World of Values
Variables
Remove Duplication
In a Single Expression
Functions
Remove Duplication
Across Multple
Expressions
Combinators
Remove Higher-Order
Duplication Across
Expressions
Testing
Test Values for
Equality, Similarity,
Inequality
Cases
Packages
Types
Fields
Input/Output
Methods
1. Intro to Functional Effects
9
● GO RUNNING
1. Intro to Functional Effects
def monitor: Boolean = {
if (sensor.tripped) {
securityCompany.call()
true
} else false
}
10
1. Intro to Functional Effects
11
sealed trait Alarm[+A]
case class Return [A](v : A) extends Alarm[A]
case class CheckTripped[A](f : Boolean => Alarm[A]) extends Alarm[A]
case class Call [A](next: Alarm[A]) extends Alarm[A]
val check: Alarm[Boolean] =
CheckTripped(tripped =>
if (tripped) Call(Return(true)) else Return(false))
1. Intro to Functional Effects
12
● GO RUNNING
Execution
(Interpretation)
1. Intro to Functional Effects
13
def interpret[A](alarm: Alarm[A]): A = alarm match {
case Return(v) => v
case CheckTripped(f) => interpret(f(sensor.tripped))
case Call(next) => securityCompany.call(); interpret(next)
}
1. Intro to Functional Effects
14
"A functional effect is an immutable data type equipped with a set
of core operations that together provide a complete, type-safe
model of a domain concern."
— John A. De Goes
1. Intro to Functional Effects
15
For every concern, there is a functional effect
Concern Effect Execution
Optionality Option[A] null or A
Disjunction Either[A, B] A or B
Nondeterminism List[A] Option[A]
Input/Output IO[A] throw or A
16
Optionality
(The Painful Way)
1. Intro to Functional Effects
17
A functional effect for optionality
Maybe[A]
Succeeds with values of type A
1. Intro to Functional Effects
18
A functional effect for optionality
Operation Signature
Present A => Maybe[A]
Absent Maybe[Nothing]
Map (Maybe[A], A => B) => Maybe[B]
Chain (Maybe[A], A => Maybe[B]) => Maybe[B]
1. Intro to Functional Effects
19
sealed trait Maybe[+A]
case class Present[A](value: A) extends Maybe[A]
case object Absent extends Maybe[Nothing]
case class Map[A, B](maybe: Maybe[A], mapper: A => B) extends Maybe[B]
case class Chain[A, B](first: Maybe[A], callback: A => Maybe[B]) extends Maybe[B]
A functional effect for optionality
1. Intro to Functional Effects
20
sealed trait Maybe[+A] { self =>
def map[B](f: A => B): Maybe[B] = Map(self, f)
def flatMap[B](f: A => Maybe[B]): Maybe[B] = Chain(self, f)
}
object Maybe {
def present[A](value: A): Maybe[A] = Present[A]
val absent: Maybe[Nothing] = Absent
}
A functional effect for optionality
1. Intro to Functional Effects
def interpret[Z, A](ifAbsent: Z, f: A => Z)(maybe: Maybe[A]): Z =
maybe match {
case Present(a) => f(a)
case Absent => ifAbsent
case Map(old, f0) => interpret(ifAbsent, f.compose(f0))(old)
case Chain(old, f) =>
interpret(ifAbsent, a => interpret(ifAbsent, f)(f(a)))(old)
}
21
A functional effect for optionality
1. Intro to Functional Effects
22
Core operations for functional effects
Operation Signature Functional Type Class
pure / point A => F[A] Applicative
empty / zero F[Nothing] MonadPlus
map (F[A], A => B) => F[B] Functor
flatMap (F[A], A => F[B]) => F[B] Monad
zip / ap [F[A], F[B]) => F[(A, B)] Apply
1. Intro to Functional Effects
23
For comprehension syntax for monadic effects
1. Intro to Functional Effects
for {
user <- lookupUser(userId)
profile <- user.profile
pic <- profile.picUrl
} yield pic
lookupUser(userId).flatMap(user =>
user.profile.flatMap(profile =>
profile.picUrl.map(pic =>
pic)))
2.
TOUR OF THE
EFFECT ZOO
24
25
The functional effect of optionality
2. Tour of the Effect Zoo
sealed trait Option[+A]
final case class Some[+A](value: A) extends Option[A]
case object None extends Option[Nothing]
Option[A]
26
The functional effect of optionality
2. Tour of the Effect Zoo
// Core operations:
def some[A](v: A): Option[A] = Some(a)
val none: Option[Nothing] = None
def map[A, B](o: Option[A], f: A => B): Option[B]
def flatMap[B, B](o: Option[A], f: A => Option[B]): Option[B]
// Execution / Interpretation:
def fold[Z](z: Z)(f: A => Z)(o: Option[A]): Z
Option[A]
27
The functional effect of optionality
2. Tour of the Effect Zoo
for {
user <- lookupUser(userId)
profile <- user.profile
pic <- profile.picUrl
} yield pic
Option[A]
28
The functional effect of failure
2. Tour of the Effect Zoo
sealed trait Either[+E, +A]
final case class Left[+E](value: E) extends Either[E, Nothing]
case class Right[+A](value: A) extends Eitherr[Nothing, A]
Either[E, A]
29
The functional effect of failure
2. Tour of the Effect Zoo
Either[E, A]
// Core operations:
def left[E](e: E): Either[E, Nothing] = Left(e)
def right[A](a: A): Either[Nothing, A] = Right(a)
def map[E, A, B](o: Either[E, A], f: A => B): Either[E, B]
def flatMap[E, A, B](o: Either[E, A], f: A => Either[E, B]): Either[E, B]
// Execution / Interpretation:
def fold[Z, E, A](left: E => Z, right: A => Z)(e: Either[E, A]): Z
30
The functional effect of failure
2. Tour of the Effect Zoo
for {
user <- decodeUser(json1)
profile <- decodeProfile(json2)
pic <- decodeImage(profile.encPic)
} yield (user, profile, pic)
Either[E, A]
31
The functional effect of logging
2. Tour of the Effect Zoo
final case class Writer[+W, +A](run: (Vector[W], A))
Writer[W, A]
32
The functional effect of logging
2. Tour of the Effect Zoo
Writer[W, A]
// Core operations:
def pure[A](a: A): Writer[Nothing, A] = Writer((Vector(), a))
def write[W](w: W): Writer[W, Unit] = Writer((Vector(w), ()))
def map[W, A, B](o: Writer[W, A], f: A => B): Writer[W, B]
def flatMap[W, A, B](o: Writerr[W, A], f: A => Writer[W, B]): Writer[W, B]
// Execution / Interpretation:
def run[W, A](writer: Writer[W, A]): (Vector[W], A)
33
The functional effect of logging
2. Tour of the Effect Zoo
for {
user <- pure(findUser())
_ <- log(s"Got user: $user")
_ <- pure(getProfile(user))
_ <- log(s"Got profile: $profile")
} yield user
Writer[W, A]
34
The functional effect of state
2. Tour of the Effect Zoo
final case class State[S, +A](run: S => (S, A))
State[S, A]
35
The functional effect of state
2. Tour of the Effect Zoo
State[S, A]
// Core operations:
def pure[S, A](a: A): State[S, A] = State[S, A](s => (s, a))
def get[S]: State[S, S] = State[S, S](s => (s, s))
def set[S](s: S): State[S, Unit] = State[S, S](_ => (s, ()))
def map[S, A, B](o: State[S, A], f: A => B): State[S, B]
def flatMap[S, A, B](o: State[S, A], f: A => State[S, B]): State[S, B]
// Execution / Interpretation:
def run[S, A](s: S, state: State[S, A]): (S, A)
36
The functional effect of state
2. Tour of the Effect Zoo
for {
_ <- set(0)
v <- get
_ <- set(v + 1)
v <- get
} yield v
State[S, A]
37
The functional effect of reader
2. Tour of the Effect Zoo
final case class Reader[-R, +A](run: R => A)
Reader[R, A]
38
The functional effect of reader
2. Tour of the Effect Zoo
Reader[R, A]
// Core operations:
def pure[A](a: A): Reader[Any, A] = Reader[Any, A](_ => a)
def environment: Reader[R, R] = Reader[R, R](r => r)
def map[R, A, B](r: Reader[R, A], f: A => B): Reader[R, B]
def flatMap[R, A, B](r: Reader[R, A], f: A => Reader[R, B]): Reader[R, B]
// Execution / Interpretation:
def provide[R, A](r: R, reader: Reader[R, A]): A
39
The functional effect of reader
2. Tour of the Effect Zoo
for {
port <- environment[Config].map(_.port)
server <- environment[Config].map(_.server)
retries <- environment[Config].map(_.retries)
} yield (port, server, retries)
Reader[R, A]
40
The functional effect of asynchronous input/output
2. Tour of the Effect Zoo
final case class IO[+A](unsafeRun: (Try[A] => Unit) => Unit)
IO[A]
41
The functional effect of asynchronous input/output
2. Tour of the Effect Zoo
// Core operations:
def sync[A](v: => A): IO[A] = IO(_(Success(v)))
def async[A](r: (Try[A] => Unit) => Unit): IO[A] = IO(r)
def fail(t: Throwable): IO[Nothing] = IO(_(Failure(t)))
def map[A, B](o: IO[A], f: A => B): IO[B]
def flatMap[B, B](o: IO[A], f: A => IO[B]): IO[B]
// Execution / Interpretation:
def unsafeRun[A](io: IO[A], k: Try[A] => Unit): Unit
IO[A]
3.
VERTICAL EFFECT
COMPOSITION
42
43
Option + Either
3. Vertical Effect Composition
44
3. Vertical Effect Composition
final case class OptionEither[+E, +A](run: Either[E, Option[A]]) {
def map[B](f: A => B): OptionEither[E, B] = ???
def flatMap[B](f: A => OptionEither[E, B]): OptionEither[E, B] = ???
}
object OptionEither {
def pure[A](a: A): OptionEither[Nothing, A] = lift(Some(a))
def lift[A](option: Option[A]): OptionEither[Nothing, A] =
OptionEither(Right(option))
}
Vertical composition of Option atop Either
45
3. Vertical Effect Composition
final case class OptionT[F[_], +A](run: F[Option[A]]) {
def map[B](f: A => B)(implicit F: Functor[F]): OptionT[F, B] = ???
def flatMap[B](f: A => OptionT[F, B])(implicit F: Monad[F]): OptionT[F, B] = ???
}
object OptionT {
def pure[A](a: A)(implicit F: Applicative[F]): OptionT[F, A] = lift(Some(a))
def lift[A](option: Option[A])(implicit F: Applicative[F]): OptionT[F, A] =
OptionT(F.pure(option))
}
Vertical composition of Option atop F[_]
46
3. Vertical Effect Composition
def myCode[F[_]: Monad]: F[Result] = {
// Introduce state effect locally:
def inner: StateT[F, MyState, Result] = ???
// Eliminate state effect locally:
inner.run(MyState.Initial)
}
Monad transformers allow local effect introduction / elimination
47
3. Vertical Effect Composition
"Monad transformers allow modular composition of separate
functional effect types into a single functional effect, with the
ability to locally introduce and eliminate effect types."
— John A. De Goes
48
Option +
Either +
Writer +
State +
Reader
3. Vertical Effect Composition
49
type GodMonad[+E, +W, S, -R, A] =
OptionT[
EitherT[
WriterT[
StateT[
Reader[R, ?],
S, ?],
W, ?],
E, ?],
?]
3. Vertical Effect Composition
50
3. Vertical Effect Composition
val effect: GodMonad[Throwable, String, Int, Config, Int] =
OptionT(
EitherT(
WriterT(
StateT(
Reader(_ => 42)))))
Monad transformers are extremely cumbersome to use directly!
51
3. Vertical Effect Composition
Monad transformers cripple performance even with opaque types!
F
OptionT[F]
StateT[EitherT[OptionT[F, ?], E, ?], S, ?]
...
52
3. Vertical Effect Composition
StateT[EitherT[F, E, ?], S, ?]
!=
EitherT[StateT[F, S, ?], E, ?]
Monad transformers are extremely order-sensitive!
n layers = n! possible orderings
53
3. Vertical Effect Composition
def myCode[F[_]: Monad]: F[Result] = {
// Cannot simply introduce / eliminate
// monad state for deeper levels!!!
def inner[G[_]: Monad](implicit G: MonadState[G, MyState, ?]): G[Result] = ???
inner[F] // Will not compile!
}
Tagless-final obliterates local effect introduction / elimination!
54
3. Vertical Effect Composition
MonadError[StateT[EitherT[IO, MyError, ?], MyState, ?], MyError, ?]
Due to encoding + compiler, transformers infer poorly!
55
3. Vertical Effect Composition
Monad transformerr checklist
Monad Transformers
Intro / Elimination ✅
Type-Inference ❌
Order-Insensitive ❌
Encapsulated ✅
Ergonomic ❌
4.
INTRO TO
EFFECT ROTATION
56
57
4. Intro to Effect Rotation
sealed trait Either[+A, +B]
case class Left [A](value: A) extends Either[A, Nothing]
case class Right[B](value: B) extends Either[Nothing, B]
Either is the simplest possible failure + success effect
58
4. Intro to Effect Rotation
// Introduction:
def fail[E](e: E): Either[E, Nothing]
// Elimination:
def catchAll[E, A](et: Either[E, A])(f: E => A): Either[Nothing, A]
Either is the simplest possible failure + success effect
Compile-time proof of elimination
59
4. Intro to Effect Rotation
final case class REither[-R, +E, +A](run: R => Either[E, A])
Why stop with error effects?
60
4. Intro to Effect Rotation
// Introduction:
def environment[R]: REither[R, Nothing, R] = REither(r => Right(r))
// Elimination:
def provide[R, E, A](r: R)(re: REither[R, E, A]): REither[Any, E, A] =
REither[Any, E, A](_ => re.run(re))
Introduction and elimination for the reader effect
Compile-time proof of elimination
61
4. Intro to Effect Rotation
final case class REither[-R, +E, +A](run: R => Either[E, A])
Naive encoding shares some overhead with transformers
Dispatch + Boxing Overhead
62
4. Intro to Effect Rotation
63
4. Intro to Effect Rotation
MTL[R, W, S, E, A]
64
4. Intro to Effect Rotation
sealed trait MTL[-R, +W, S, +E, +A] { self =>
def map[B](f: A => B): MTL[R, W, S, E, B] = ???
def flatMap[..](f: A => MTL[R1, W1, S, E1, B]): MTL[R1, W1, S, E1, B] = ???
// Optimized interpreter for GADT:
def run(r: R, s: S): (List[W], S, Either[E, A]) = ???
}
Possible to eliminate overhead with "free" encodings
65
4. Intro to Effect Rotation
object MTL {
final case class Succeed[S, +A](value: A) extends MTL[Any, Nothing, S, Nothing, A]
final case class Reader [R, S]() extends MTL[R, Nothing, S, Nothing, R]
final case class RunReader[R, +W, S, +E, +A](r: R, mtl: MTL[R, W, S, E, A]) extends MTL[Any, W, S, E, A]
final case class Error [S, +E](error: E) extends MTL[Any, Nothing, S, E, Nothing]
final case class RunError[-R, +W, S, +E, +A](mtl: MTL[R, W, S, E, A]) extends MTL[Any, W, S, Nothing, Either[E, A]]
final case class State [S, +A](f: S => (S, A)) extends MTL[Any, Nothing, S, Nothing, A]
final case class RunState[-R, +W, S, +E, +A](s: S, mtl: MTL[R, W, S, E, A]) extends MTL[R, W, Unit, E, A]
final case class Writer [+W, S](w: W) extends MTL[Any, W, S, Nothing, Unit]
final case class RunWriter[-R, +W, S, +E, +A](mtl: MTL[R, W, S, E, A]) extends MTL[R, Nothing, S, E, (List[W], A)]
final case class FlatMap[R, W, S, E, A, R1 <: R, W1 >: W, E1 >: E, B](first: MTL[R, W, S, E, A], k: A => MTL[R1, W1, S, E1, B]) extends
MTL[R1, W1, S, E1, B]
}
Possible to eliminate overhead with "free" encodings
66
4. Intro to Effect Rotation
Elimination laws for effect rotation
Effect Type Type Elimination Examples
Covariant Nothing Failure, Optionality,
Writer
Contravariant Any Reader
Invariant Unit State
67
4. Intro to Effect Rotation
Monad transformers versus effect rotation
Monad Transformers Effect Rotation
Intro / Elimination ✅ ✅
Type-Inference ❌ ✅
Order-Insensitive ❌ ✅
Encapsulated ✅ ❌
Ergonomic ❌ ✅
5.
PRACTICE OF
EFFECT ROTATION
68
69
5. Practice of Effect Rotation
ZIO[R, E, A]
Reader
Error
Success
70
5. Practice of Effect Rotation
Reader
trait ZIO[R, E, A] {
def provide(r: R): ZIO[Any, E, A] = ???
}
object ZIO {
def environment: ZIO[R, Nothing, R] = ???
def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ???
}
71
5. Practice of Effect Rotation
Error
trait ZIO[R, E, A] {
def either: ZIO[R, Nothing, Either[E, A]] = ???
}
object ZIO {
def fail(e: E): ZIO[Any, E, Nothing] = ???
def succeed(a: A): ZIO[Any, Nothing, A] = ???
}
72
5. Practice of Effect Rotation
Option via Error
ZIO[R, Unit, A]
73
5. Practice of Effect Rotation
Option + Either via Error
ZIO[R, Option[E], A]
74
5. Practice of Effect Rotation
trait Ref[A] {
def update(f: A => A): UIO[A]
def modify[B](f: A => (B, A)): UIO[B]
def get: UIO[A]
def set(a: A): UIO[Unit]
}
Reader + Ref / TRef is the key to unlocking other effects!
75
5. Practice of Effect Rotation
Writer via Reader
trait Writer[W] {
def writer: Ref[Vector[W]]
}
def write[W](w: W): ZIO[Writer[W], Nothing, Unit] =
ZIO.accessM[Writer[W]](_.writer.update(_ :+ w).unit)
76
5. Practice of Effect Rotation
State via Reader
trait State[S] {
def state: Ref[S]
}
def modify[S, A](f: S => (S, A)): ZIO[State[S], Nothing, A] =
ZIO.accessM[State[S]](_.state.modify(f))
def update[S, A](f: S => S): ZIO[State[S], Nothing, Unit] = modify(s => (s, ()))
def gets[S]: ZIO[State[S], Nothing, S] = modify(s => (s, s))
77
5. Practice of Effect Rotation
God Monad
type Fx[S, W] = State[S] with Writer[W]
type GodMonad2[+E, W, S, -R <: Fx[S, W], A] =
ZIO[R, Option[E], A]
78
5. Practice of Effect Rotation
Effect rotation delivers resource / concurrent safety!
Monad Transformers Effect Rotation
... ... ...
Concurrent-Safe State ❌ ✅
Concurrrent Safe Writer ❌ ✅
Resource-Safe State ❌ ✅
Resource-Safe Writer ❌ ✅
THANK YOU!
Any questions?
You should follow me on Twitter:
@jdegoes
And bookmark my blog:
http://degoes.net
79

Weitere ähnliche Inhalte

Was ist angesagt?

Was ist angesagt? (20)

The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)
 
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
 
Error Management: Future vs ZIO
Error Management: Future vs ZIOError Management: Future vs ZIO
Error Management: Future vs ZIO
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorld
 
Javascript Arrow function
Javascript Arrow functionJavascript Arrow function
Javascript Arrow function
 
Applicative Functor
Applicative FunctorApplicative Functor
Applicative Functor
 
Functional Core and Imperative Shell - Game of Life Example - Haskell and Scala
Functional Core and Imperative Shell - Game of Life Example - Haskell and ScalaFunctional Core and Imperative Shell - Game of Life Example - Haskell and Scala
Functional Core and Imperative Shell - Game of Life Example - Haskell and Scala
 
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
 
Izumi 1.0: Your Next Scala Stack
Izumi 1.0: Your Next Scala StackIzumi 1.0: Your Next Scala Stack
Izumi 1.0: Your Next Scala Stack
 
Big picture of category theory in scala with deep dive into contravariant and...
Big picture of category theory in scala with deep dive into contravariant and...Big picture of category theory in scala with deep dive into contravariant and...
Big picture of category theory in scala with deep dive into contravariant and...
 
Lambda Expressions in Java 8
Lambda Expressions in Java 8Lambda Expressions in Java 8
Lambda Expressions in Java 8
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't Free
 
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
OOP and FP
OOP and FPOOP and FP
OOP and FP
 
Cracking OCA and OCP Java 8 Exams
Cracking OCA and OCP Java 8 ExamsCracking OCA and OCP Java 8 Exams
Cracking OCA and OCP Java 8 Exams
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
 
Ad hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and CatsAd hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and Cats
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 

Ähnlich wie One Monad to Rule Them All

Monads do not Compose
Monads do not ComposeMonads do not Compose
Monads do not Compose
Philip Schwarz
 

Ähnlich wie One Monad to Rule Them All (20)

Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
 
Zio from Home
Zio from Home Zio from Home
Zio from Home
 
Berlin meetup
Berlin meetupBerlin meetup
Berlin meetup
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
Functions, Types, Programs and Effects
Functions, Types, Programs and EffectsFunctions, Types, Programs and Effects
Functions, Types, Programs and Effects
 
Introductory part of function for class 12th JEE
Introductory part of function for class 12th JEEIntroductory part of function for class 12th JEE
Introductory part of function for class 12th JEE
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
 
Talk - Query monad
Talk - Query monad Talk - Query monad
Talk - Query monad
 
Algebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain ModelsAlgebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain Models
 
Actors and functional_reactive_programming
Actors and functional_reactive_programmingActors and functional_reactive_programming
Actors and functional_reactive_programming
 
Taking your side effects aside
Taking your side effects asideTaking your side effects aside
Taking your side effects aside
 
Kotlin For Android - Functions (part 3 of 7)
Kotlin For Android - Functions (part 3 of 7)Kotlin For Android - Functions (part 3 of 7)
Kotlin For Android - Functions (part 3 of 7)
 
Fp in scala with adts
Fp in scala with adtsFp in scala with adts
Fp in scala with adts
 
Monads do not Compose
Monads do not ComposeMonads do not Compose
Monads do not Compose
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
 
Composition birds-and-recursion
Composition birds-and-recursionComposition birds-and-recursion
Composition birds-and-recursion
 
Functional IO and Effects
Functional IO and EffectsFunctional IO and Effects
Functional IO and Effects
 
Python programming workshop session 3
Python programming workshop session 3Python programming workshop session 3
Python programming workshop session 3
 
Advanced JavaScript
Advanced JavaScript Advanced JavaScript
Advanced JavaScript
 
Why functional programming and category theory strongly matters - Piotr Parad...
Why functional programming and category theory strongly matters - Piotr Parad...Why functional programming and category theory strongly matters - Piotr Parad...
Why functional programming and category theory strongly matters - Piotr Parad...
 

Mehr von John De Goes

Refactoring Functional Type Classes
Refactoring Functional Type ClassesRefactoring Functional Type Classes
Refactoring Functional Type Classes
John De Goes
 
The Death of Final Tagless
The Death of Final TaglessThe Death of Final Tagless
The Death of Final Tagless
John De Goes
 
Orthogonal Functional Architecture
Orthogonal Functional ArchitectureOrthogonal Functional Architecture
Orthogonal Functional Architecture
John De Goes
 
Post-Free: Life After Free Monads
Post-Free: Life After Free MonadsPost-Free: Life After Free Monads
Post-Free: Life After Free Monads
John De Goes
 

Mehr von John De Goes (20)

Refactoring Functional Type Classes
Refactoring Functional Type ClassesRefactoring Functional Type Classes
Refactoring Functional Type Classes
 
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
 
The Death of Final Tagless
The Death of Final TaglessThe Death of Final Tagless
The Death of Final Tagless
 
Scalaz Stream: Rebirth
Scalaz Stream: RebirthScalaz Stream: Rebirth
Scalaz Stream: Rebirth
 
Scalaz Stream: Rebirth
Scalaz Stream: RebirthScalaz Stream: Rebirth
Scalaz Stream: Rebirth
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Scalaz 8: A Whole New Game
Scalaz 8: A Whole New GameScalaz 8: A Whole New Game
Scalaz 8: A Whole New Game
 
Scalaz 8 vs Akka Actors
Scalaz 8 vs Akka ActorsScalaz 8 vs Akka Actors
Scalaz 8 vs Akka Actors
 
Orthogonal Functional Architecture
Orthogonal Functional ArchitectureOrthogonal Functional Architecture
Orthogonal Functional Architecture
 
The Design of the Scalaz 8 Effect System
The Design of the Scalaz 8 Effect SystemThe Design of the Scalaz 8 Effect System
The Design of the Scalaz 8 Effect System
 
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & AnalyticsQuark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
 
Post-Free: Life After Free Monads
Post-Free: Life After Free MonadsPost-Free: Life After Free Monads
Post-Free: Life After Free Monads
 
Streams for (Co)Free!
Streams for (Co)Free!Streams for (Co)Free!
Streams for (Co)Free!
 
MTL Versus Free
MTL Versus FreeMTL Versus Free
MTL Versus Free
 
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
 
Halogen: Past, Present, and Future
Halogen: Past, Present, and FutureHalogen: Past, Present, and Future
Halogen: Past, Present, and Future
 
All Aboard The Scala-to-PureScript Express!
All Aboard The Scala-to-PureScript Express!All Aboard The Scala-to-PureScript Express!
All Aboard The Scala-to-PureScript Express!
 
Getting Started with PureScript
Getting Started with PureScriptGetting Started with PureScript
Getting Started with PureScript
 
SlamData - How MongoDB Is Powering a Revolution in Visual Analytics
SlamData - How MongoDB Is Powering a Revolution in Visual AnalyticsSlamData - How MongoDB Is Powering a Revolution in Visual Analytics
SlamData - How MongoDB Is Powering a Revolution in Visual Analytics
 
The Next Great Functional Programming Language
The Next Great Functional Programming LanguageThe Next Great Functional Programming Language
The Next Great Functional Programming Language
 

Kürzlich hochgeladen

Kürzlich hochgeladen (20)

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 

One Monad to Rule Them All

  • 1. One Monad to Rule Them All Functional JVM Meetup Prague, Aug 8 2019 John A. De Goes — @jdegoes
  • 2. Agenda 1. INTRO TO FUNCTIONAL EFFECTS 2 2. TOUR OF THE EFFECT ZOO 3. VERTICAL EFFECT COMPOSITION 4. INTRO TO EFFECT ROTATION 5. PRACTICE OF EFFECT ROTATION
  • 4. 1. Intro to Functional Effects 4 World of Values Variables Remove Duplication In a Single Expression Functions Remove Duplication Across Multple Expressions Combinators Remove Higher-Order Duplication Across Expressions Testing Test Values for Equality, Similarity, Inequality
  • 5. 1. Intro to Functional Effects 5 World of Values Variables Remove Duplication In a Single Expression Functions Remove Duplication Across Multple Expressions Combinators Remove Higher-Order Duplication Across Expressions Testing Test Values for Equality, Similarity, Inequality
  • 6. 1. Intro to Functional Effects 6 World of Values Variables Remove Duplication In a Single Expression Functions Remove Duplication Across Multple Expressions Combinators Remove Higher-Order Duplication Across Expressions Testing Test Values for Equality, Similarity, Inequality
  • 7. 1. Intro to Functional Effects 7 World of Values Variables Remove Duplication In a Single Expression Functions Remove Duplication Across Multple Expressions Combinators Remove Higher-Order Duplication Across Expressions Testing Test Values for Equality, Similarity, Inequality
  • 8. 1. Intro to Functional Effects 8 World of Values Variables Remove Duplication In a Single Expression Functions Remove Duplication Across Multple Expressions Combinators Remove Higher-Order Duplication Across Expressions Testing Test Values for Equality, Similarity, Inequality Cases Packages Types Fields Input/Output Methods
  • 9. 1. Intro to Functional Effects 9 ● GO RUNNING
  • 10. 1. Intro to Functional Effects def monitor: Boolean = { if (sensor.tripped) { securityCompany.call() true } else false } 10
  • 11. 1. Intro to Functional Effects 11 sealed trait Alarm[+A] case class Return [A](v : A) extends Alarm[A] case class CheckTripped[A](f : Boolean => Alarm[A]) extends Alarm[A] case class Call [A](next: Alarm[A]) extends Alarm[A] val check: Alarm[Boolean] = CheckTripped(tripped => if (tripped) Call(Return(true)) else Return(false))
  • 12. 1. Intro to Functional Effects 12 ● GO RUNNING Execution (Interpretation)
  • 13. 1. Intro to Functional Effects 13 def interpret[A](alarm: Alarm[A]): A = alarm match { case Return(v) => v case CheckTripped(f) => interpret(f(sensor.tripped)) case Call(next) => securityCompany.call(); interpret(next) }
  • 14. 1. Intro to Functional Effects 14 "A functional effect is an immutable data type equipped with a set of core operations that together provide a complete, type-safe model of a domain concern." — John A. De Goes
  • 15. 1. Intro to Functional Effects 15 For every concern, there is a functional effect Concern Effect Execution Optionality Option[A] null or A Disjunction Either[A, B] A or B Nondeterminism List[A] Option[A] Input/Output IO[A] throw or A
  • 16. 16 Optionality (The Painful Way) 1. Intro to Functional Effects
  • 17. 17 A functional effect for optionality Maybe[A] Succeeds with values of type A 1. Intro to Functional Effects
  • 18. 18 A functional effect for optionality Operation Signature Present A => Maybe[A] Absent Maybe[Nothing] Map (Maybe[A], A => B) => Maybe[B] Chain (Maybe[A], A => Maybe[B]) => Maybe[B] 1. Intro to Functional Effects
  • 19. 19 sealed trait Maybe[+A] case class Present[A](value: A) extends Maybe[A] case object Absent extends Maybe[Nothing] case class Map[A, B](maybe: Maybe[A], mapper: A => B) extends Maybe[B] case class Chain[A, B](first: Maybe[A], callback: A => Maybe[B]) extends Maybe[B] A functional effect for optionality 1. Intro to Functional Effects
  • 20. 20 sealed trait Maybe[+A] { self => def map[B](f: A => B): Maybe[B] = Map(self, f) def flatMap[B](f: A => Maybe[B]): Maybe[B] = Chain(self, f) } object Maybe { def present[A](value: A): Maybe[A] = Present[A] val absent: Maybe[Nothing] = Absent } A functional effect for optionality 1. Intro to Functional Effects
  • 21. def interpret[Z, A](ifAbsent: Z, f: A => Z)(maybe: Maybe[A]): Z = maybe match { case Present(a) => f(a) case Absent => ifAbsent case Map(old, f0) => interpret(ifAbsent, f.compose(f0))(old) case Chain(old, f) => interpret(ifAbsent, a => interpret(ifAbsent, f)(f(a)))(old) } 21 A functional effect for optionality 1. Intro to Functional Effects
  • 22. 22 Core operations for functional effects Operation Signature Functional Type Class pure / point A => F[A] Applicative empty / zero F[Nothing] MonadPlus map (F[A], A => B) => F[B] Functor flatMap (F[A], A => F[B]) => F[B] Monad zip / ap [F[A], F[B]) => F[(A, B)] Apply 1. Intro to Functional Effects
  • 23. 23 For comprehension syntax for monadic effects 1. Intro to Functional Effects for { user <- lookupUser(userId) profile <- user.profile pic <- profile.picUrl } yield pic lookupUser(userId).flatMap(user => user.profile.flatMap(profile => profile.picUrl.map(pic => pic)))
  • 25. 25 The functional effect of optionality 2. Tour of the Effect Zoo sealed trait Option[+A] final case class Some[+A](value: A) extends Option[A] case object None extends Option[Nothing] Option[A]
  • 26. 26 The functional effect of optionality 2. Tour of the Effect Zoo // Core operations: def some[A](v: A): Option[A] = Some(a) val none: Option[Nothing] = None def map[A, B](o: Option[A], f: A => B): Option[B] def flatMap[B, B](o: Option[A], f: A => Option[B]): Option[B] // Execution / Interpretation: def fold[Z](z: Z)(f: A => Z)(o: Option[A]): Z Option[A]
  • 27. 27 The functional effect of optionality 2. Tour of the Effect Zoo for { user <- lookupUser(userId) profile <- user.profile pic <- profile.picUrl } yield pic Option[A]
  • 28. 28 The functional effect of failure 2. Tour of the Effect Zoo sealed trait Either[+E, +A] final case class Left[+E](value: E) extends Either[E, Nothing] case class Right[+A](value: A) extends Eitherr[Nothing, A] Either[E, A]
  • 29. 29 The functional effect of failure 2. Tour of the Effect Zoo Either[E, A] // Core operations: def left[E](e: E): Either[E, Nothing] = Left(e) def right[A](a: A): Either[Nothing, A] = Right(a) def map[E, A, B](o: Either[E, A], f: A => B): Either[E, B] def flatMap[E, A, B](o: Either[E, A], f: A => Either[E, B]): Either[E, B] // Execution / Interpretation: def fold[Z, E, A](left: E => Z, right: A => Z)(e: Either[E, A]): Z
  • 30. 30 The functional effect of failure 2. Tour of the Effect Zoo for { user <- decodeUser(json1) profile <- decodeProfile(json2) pic <- decodeImage(profile.encPic) } yield (user, profile, pic) Either[E, A]
  • 31. 31 The functional effect of logging 2. Tour of the Effect Zoo final case class Writer[+W, +A](run: (Vector[W], A)) Writer[W, A]
  • 32. 32 The functional effect of logging 2. Tour of the Effect Zoo Writer[W, A] // Core operations: def pure[A](a: A): Writer[Nothing, A] = Writer((Vector(), a)) def write[W](w: W): Writer[W, Unit] = Writer((Vector(w), ())) def map[W, A, B](o: Writer[W, A], f: A => B): Writer[W, B] def flatMap[W, A, B](o: Writerr[W, A], f: A => Writer[W, B]): Writer[W, B] // Execution / Interpretation: def run[W, A](writer: Writer[W, A]): (Vector[W], A)
  • 33. 33 The functional effect of logging 2. Tour of the Effect Zoo for { user <- pure(findUser()) _ <- log(s"Got user: $user") _ <- pure(getProfile(user)) _ <- log(s"Got profile: $profile") } yield user Writer[W, A]
  • 34. 34 The functional effect of state 2. Tour of the Effect Zoo final case class State[S, +A](run: S => (S, A)) State[S, A]
  • 35. 35 The functional effect of state 2. Tour of the Effect Zoo State[S, A] // Core operations: def pure[S, A](a: A): State[S, A] = State[S, A](s => (s, a)) def get[S]: State[S, S] = State[S, S](s => (s, s)) def set[S](s: S): State[S, Unit] = State[S, S](_ => (s, ())) def map[S, A, B](o: State[S, A], f: A => B): State[S, B] def flatMap[S, A, B](o: State[S, A], f: A => State[S, B]): State[S, B] // Execution / Interpretation: def run[S, A](s: S, state: State[S, A]): (S, A)
  • 36. 36 The functional effect of state 2. Tour of the Effect Zoo for { _ <- set(0) v <- get _ <- set(v + 1) v <- get } yield v State[S, A]
  • 37. 37 The functional effect of reader 2. Tour of the Effect Zoo final case class Reader[-R, +A](run: R => A) Reader[R, A]
  • 38. 38 The functional effect of reader 2. Tour of the Effect Zoo Reader[R, A] // Core operations: def pure[A](a: A): Reader[Any, A] = Reader[Any, A](_ => a) def environment: Reader[R, R] = Reader[R, R](r => r) def map[R, A, B](r: Reader[R, A], f: A => B): Reader[R, B] def flatMap[R, A, B](r: Reader[R, A], f: A => Reader[R, B]): Reader[R, B] // Execution / Interpretation: def provide[R, A](r: R, reader: Reader[R, A]): A
  • 39. 39 The functional effect of reader 2. Tour of the Effect Zoo for { port <- environment[Config].map(_.port) server <- environment[Config].map(_.server) retries <- environment[Config].map(_.retries) } yield (port, server, retries) Reader[R, A]
  • 40. 40 The functional effect of asynchronous input/output 2. Tour of the Effect Zoo final case class IO[+A](unsafeRun: (Try[A] => Unit) => Unit) IO[A]
  • 41. 41 The functional effect of asynchronous input/output 2. Tour of the Effect Zoo // Core operations: def sync[A](v: => A): IO[A] = IO(_(Success(v))) def async[A](r: (Try[A] => Unit) => Unit): IO[A] = IO(r) def fail(t: Throwable): IO[Nothing] = IO(_(Failure(t))) def map[A, B](o: IO[A], f: A => B): IO[B] def flatMap[B, B](o: IO[A], f: A => IO[B]): IO[B] // Execution / Interpretation: def unsafeRun[A](io: IO[A], k: Try[A] => Unit): Unit IO[A]
  • 43. 43 Option + Either 3. Vertical Effect Composition
  • 44. 44 3. Vertical Effect Composition final case class OptionEither[+E, +A](run: Either[E, Option[A]]) { def map[B](f: A => B): OptionEither[E, B] = ??? def flatMap[B](f: A => OptionEither[E, B]): OptionEither[E, B] = ??? } object OptionEither { def pure[A](a: A): OptionEither[Nothing, A] = lift(Some(a)) def lift[A](option: Option[A]): OptionEither[Nothing, A] = OptionEither(Right(option)) } Vertical composition of Option atop Either
  • 45. 45 3. Vertical Effect Composition final case class OptionT[F[_], +A](run: F[Option[A]]) { def map[B](f: A => B)(implicit F: Functor[F]): OptionT[F, B] = ??? def flatMap[B](f: A => OptionT[F, B])(implicit F: Monad[F]): OptionT[F, B] = ??? } object OptionT { def pure[A](a: A)(implicit F: Applicative[F]): OptionT[F, A] = lift(Some(a)) def lift[A](option: Option[A])(implicit F: Applicative[F]): OptionT[F, A] = OptionT(F.pure(option)) } Vertical composition of Option atop F[_]
  • 46. 46 3. Vertical Effect Composition def myCode[F[_]: Monad]: F[Result] = { // Introduce state effect locally: def inner: StateT[F, MyState, Result] = ??? // Eliminate state effect locally: inner.run(MyState.Initial) } Monad transformers allow local effect introduction / elimination
  • 47. 47 3. Vertical Effect Composition "Monad transformers allow modular composition of separate functional effect types into a single functional effect, with the ability to locally introduce and eliminate effect types." — John A. De Goes
  • 48. 48 Option + Either + Writer + State + Reader 3. Vertical Effect Composition
  • 49. 49 type GodMonad[+E, +W, S, -R, A] = OptionT[ EitherT[ WriterT[ StateT[ Reader[R, ?], S, ?], W, ?], E, ?], ?] 3. Vertical Effect Composition
  • 50. 50 3. Vertical Effect Composition val effect: GodMonad[Throwable, String, Int, Config, Int] = OptionT( EitherT( WriterT( StateT( Reader(_ => 42))))) Monad transformers are extremely cumbersome to use directly!
  • 51. 51 3. Vertical Effect Composition Monad transformers cripple performance even with opaque types! F OptionT[F] StateT[EitherT[OptionT[F, ?], E, ?], S, ?] ...
  • 52. 52 3. Vertical Effect Composition StateT[EitherT[F, E, ?], S, ?] != EitherT[StateT[F, S, ?], E, ?] Monad transformers are extremely order-sensitive! n layers = n! possible orderings
  • 53. 53 3. Vertical Effect Composition def myCode[F[_]: Monad]: F[Result] = { // Cannot simply introduce / eliminate // monad state for deeper levels!!! def inner[G[_]: Monad](implicit G: MonadState[G, MyState, ?]): G[Result] = ??? inner[F] // Will not compile! } Tagless-final obliterates local effect introduction / elimination!
  • 54. 54 3. Vertical Effect Composition MonadError[StateT[EitherT[IO, MyError, ?], MyState, ?], MyError, ?] Due to encoding + compiler, transformers infer poorly!
  • 55. 55 3. Vertical Effect Composition Monad transformerr checklist Monad Transformers Intro / Elimination ✅ Type-Inference ❌ Order-Insensitive ❌ Encapsulated ✅ Ergonomic ❌
  • 57. 57 4. Intro to Effect Rotation sealed trait Either[+A, +B] case class Left [A](value: A) extends Either[A, Nothing] case class Right[B](value: B) extends Either[Nothing, B] Either is the simplest possible failure + success effect
  • 58. 58 4. Intro to Effect Rotation // Introduction: def fail[E](e: E): Either[E, Nothing] // Elimination: def catchAll[E, A](et: Either[E, A])(f: E => A): Either[Nothing, A] Either is the simplest possible failure + success effect Compile-time proof of elimination
  • 59. 59 4. Intro to Effect Rotation final case class REither[-R, +E, +A](run: R => Either[E, A]) Why stop with error effects?
  • 60. 60 4. Intro to Effect Rotation // Introduction: def environment[R]: REither[R, Nothing, R] = REither(r => Right(r)) // Elimination: def provide[R, E, A](r: R)(re: REither[R, E, A]): REither[Any, E, A] = REither[Any, E, A](_ => re.run(re)) Introduction and elimination for the reader effect Compile-time proof of elimination
  • 61. 61 4. Intro to Effect Rotation final case class REither[-R, +E, +A](run: R => Either[E, A]) Naive encoding shares some overhead with transformers Dispatch + Boxing Overhead
  • 62. 62 4. Intro to Effect Rotation
  • 63. 63 4. Intro to Effect Rotation MTL[R, W, S, E, A]
  • 64. 64 4. Intro to Effect Rotation sealed trait MTL[-R, +W, S, +E, +A] { self => def map[B](f: A => B): MTL[R, W, S, E, B] = ??? def flatMap[..](f: A => MTL[R1, W1, S, E1, B]): MTL[R1, W1, S, E1, B] = ??? // Optimized interpreter for GADT: def run(r: R, s: S): (List[W], S, Either[E, A]) = ??? } Possible to eliminate overhead with "free" encodings
  • 65. 65 4. Intro to Effect Rotation object MTL { final case class Succeed[S, +A](value: A) extends MTL[Any, Nothing, S, Nothing, A] final case class Reader [R, S]() extends MTL[R, Nothing, S, Nothing, R] final case class RunReader[R, +W, S, +E, +A](r: R, mtl: MTL[R, W, S, E, A]) extends MTL[Any, W, S, E, A] final case class Error [S, +E](error: E) extends MTL[Any, Nothing, S, E, Nothing] final case class RunError[-R, +W, S, +E, +A](mtl: MTL[R, W, S, E, A]) extends MTL[Any, W, S, Nothing, Either[E, A]] final case class State [S, +A](f: S => (S, A)) extends MTL[Any, Nothing, S, Nothing, A] final case class RunState[-R, +W, S, +E, +A](s: S, mtl: MTL[R, W, S, E, A]) extends MTL[R, W, Unit, E, A] final case class Writer [+W, S](w: W) extends MTL[Any, W, S, Nothing, Unit] final case class RunWriter[-R, +W, S, +E, +A](mtl: MTL[R, W, S, E, A]) extends MTL[R, Nothing, S, E, (List[W], A)] final case class FlatMap[R, W, S, E, A, R1 <: R, W1 >: W, E1 >: E, B](first: MTL[R, W, S, E, A], k: A => MTL[R1, W1, S, E1, B]) extends MTL[R1, W1, S, E1, B] } Possible to eliminate overhead with "free" encodings
  • 66. 66 4. Intro to Effect Rotation Elimination laws for effect rotation Effect Type Type Elimination Examples Covariant Nothing Failure, Optionality, Writer Contravariant Any Reader Invariant Unit State
  • 67. 67 4. Intro to Effect Rotation Monad transformers versus effect rotation Monad Transformers Effect Rotation Intro / Elimination ✅ ✅ Type-Inference ❌ ✅ Order-Insensitive ❌ ✅ Encapsulated ✅ ❌ Ergonomic ❌ ✅
  • 69. 69 5. Practice of Effect Rotation ZIO[R, E, A] Reader Error Success
  • 70. 70 5. Practice of Effect Rotation Reader trait ZIO[R, E, A] { def provide(r: R): ZIO[Any, E, A] = ??? } object ZIO { def environment: ZIO[R, Nothing, R] = ??? def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ??? }
  • 71. 71 5. Practice of Effect Rotation Error trait ZIO[R, E, A] { def either: ZIO[R, Nothing, Either[E, A]] = ??? } object ZIO { def fail(e: E): ZIO[Any, E, Nothing] = ??? def succeed(a: A): ZIO[Any, Nothing, A] = ??? }
  • 72. 72 5. Practice of Effect Rotation Option via Error ZIO[R, Unit, A]
  • 73. 73 5. Practice of Effect Rotation Option + Either via Error ZIO[R, Option[E], A]
  • 74. 74 5. Practice of Effect Rotation trait Ref[A] { def update(f: A => A): UIO[A] def modify[B](f: A => (B, A)): UIO[B] def get: UIO[A] def set(a: A): UIO[Unit] } Reader + Ref / TRef is the key to unlocking other effects!
  • 75. 75 5. Practice of Effect Rotation Writer via Reader trait Writer[W] { def writer: Ref[Vector[W]] } def write[W](w: W): ZIO[Writer[W], Nothing, Unit] = ZIO.accessM[Writer[W]](_.writer.update(_ :+ w).unit)
  • 76. 76 5. Practice of Effect Rotation State via Reader trait State[S] { def state: Ref[S] } def modify[S, A](f: S => (S, A)): ZIO[State[S], Nothing, A] = ZIO.accessM[State[S]](_.state.modify(f)) def update[S, A](f: S => S): ZIO[State[S], Nothing, Unit] = modify(s => (s, ())) def gets[S]: ZIO[State[S], Nothing, S] = modify(s => (s, s))
  • 77. 77 5. Practice of Effect Rotation God Monad type Fx[S, W] = State[S] with Writer[W] type GodMonad2[+E, W, S, -R <: Fx[S, W], A] = ZIO[R, Option[E], A]
  • 78. 78 5. Practice of Effect Rotation Effect rotation delivers resource / concurrent safety! Monad Transformers Effect Rotation ... ... ... Concurrent-Safe State ❌ ✅ Concurrrent Safe Writer ❌ ✅ Resource-Safe State ❌ ✅ Resource-Safe Writer ❌ ✅
  • 79. THANK YOU! Any questions? You should follow me on Twitter: @jdegoes And bookmark my blog: http://degoes.net 79