SlideShare una empresa de Scribd logo
1 de 127
Descargar para leer sin conexión
A Prelude of Purity
Scaling Back ZIO
ZIO World
March 11th, 2022
Jorge Vásquez
Scala Developer
@Scalac
ZIO Prelude contributor
Background
ZIO Prelude is a Scala-first take on functional abstractions
Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to describe the ways different types are
similar
Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to describe the ways different types are
similar
• Smart Types for more precise data modelling
Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to describe the ways different types are
similar
• Smart Types for more precise data modelling
• Data types that complement the Scala standard library:
ZPure
Problem
Using Purely Functional style, how
can we write computations that:
Problem
Using Purely Functional style, how
can we write computations that:
• Are Stateful
Problem
Using Purely Functional style, how
can we write computations that:
• Are Stateful
• Require a Context
Problem
Using Purely Functional style, how
can we write computations that:
• Are Stateful
• Require a Context
• Might Fail or Succeed with a
value
Problem
Using Purely Functional style, how
can we write computations that:
• Are Stateful
• Require a Context
• Might Fail or Succeed with a
value
• Produce Logs
Scenario
Reverse Polish
Notation (RPN)
Calculator
Example
"2 5 + 3 * 1 -" -> 20
First attempt: Using Cats State Monad
import cats.data._
import cats.implicits._
type Stack = List[Int]
type Eff[A] = State[Stack, A]
def push(x: Int): Eff[Unit] = State.modify(x :: _)
val pop: Eff[Int] =
for {
stack <- State.get
x <- State.set(stack.tail).as(stack.head)
} yield x
def getExprElements(expr: String): List[String] = expr.split(' ').toList
First attempt: Using Cats State Monad
import cats.data._
import cats.implicits._
type Stack = List[Int]
type Eff[A] = State[Stack, A]
def push(x: Int): Eff[Unit] = State.modify(x :: _)
val pop: Eff[Int] =
for {
stack <- State.get
x <- State.set(stack.tail).as(stack.head)
} yield x
def getExprElements(expr: String): List[String] = expr.split(' ').toList
First attempt: Using Cats State Monad
import cats.data._
import cats.implicits._
type Stack = List[Int]
type Eff[A] = State[Stack, A]
def push(x: Int): Eff[Unit] = State.modify(x :: _)
val pop: Eff[Int] =
for {
stack <- State.get
x <- State.set(stack.tail).as(stack.head)
} yield x
def getExprElements(expr: String): List[String] = expr.split(' ').toList
First attempt: Using Cats State Monad
import cats.data._
import cats.implicits._
type Stack = List[Int]
type Eff[A] = State[Stack, A]
def push(x: Int): Eff[Unit] = State.modify(x :: _)
val pop: Eff[Int] =
for {
stack <- State.get
x <- State.set(stack.tail).as(stack.head)
} yield x
def getExprElements(expr: String): List[String] = expr.split(' ').toList
First attempt: Using Cats State Monad
import cats.data._
import cats.implicits._
type Stack = List[Int]
type Eff[A] = State[Stack, A]
def push(x: Int): Eff[Unit] = State.modify(x :: _)
val pop: Eff[Int] =
for {
stack <- State.get
x <- State.set(stack.tail).as(stack.head)
} yield x
def getExprElements(expr: String): List[String] = expr.split(' ').toList
First attempt: Using Cats State Monad
import cats.data._
import cats.implicits._
type Stack = List[Int]
type Eff[A] = State[Stack, A]
def push(x: Int): Eff[Unit] = State.modify(x :: _)
val pop: Eff[Int] =
for {
stack <- State.get
x <- State.set(stack.tail).as(stack.head)
} yield x
def getExprElements(expr: String): List[String] = expr.split(' ').toList
First attempt: Using Cats State Monad
import cats.data._
import cats.implicits._
type Stack = List[Int]
type Eff[A] = State[Stack, A]
def push(x: Int): Eff[Unit] = State.modify(x :: _)
val pop: Eff[Int] =
for {
stack <- State.get
x <- State.set(stack.tail).as(stack.head)
} yield x
def getExprElements(expr: String): List[String] = expr.split(' ').toList
First attempt: Using
Cats State Monad
def evalRPNExpression(elements: List[String]): Int = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => push(x.toInt)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
_ <- push(operator(second, first))
} yield ()
(elements.traverse(processElement) *> pop).runA(Nil).value
}
First attempt: Using
Cats State Monad
def evalRPNExpression(elements: List[String]): Int = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => push(x.toInt)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
_ <- push(operator(second, first))
} yield ()
(elements.traverse(processElement) *> pop).runA(Nil).value
}
First attempt: Using
Cats State Monad
def evalRPNExpression(elements: List[String]): Int = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => push(x.toInt)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
_ <- push(operator(second, first))
} yield ()
(elements.traverse(processElement) *> pop).runA(Nil).value
}
First attempt: Using
Cats State Monad
def evalRPNExpression(elements: List[String]): Int = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => push(x.toInt)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
_ <- push(operator(second, first))
} yield ()
(elements.traverse(processElement) *> pop).runA(Nil).value
}
First attempt: Using Cats State Monad
evalRPNExpression("1 3 + 10 -") // -6
evalRPNExpression("1 3 + 10 - +") // java.lang.UnsupportedOperationException: tail of empty list
evalRPNExpression("1 3 + 10 - a") // java.lang.NumberFormatException: For input string: "a"
First attempt: Using Cats State Monad
evalRPNExpression("1 3 + 10 -") // -6
evalRPNExpression("1 3 + 10 - +") // java.lang.UnsupportedOperationException: tail of empty list
evalRPNExpression("1 3 + 10 - a") // java.lang.NumberFormatException: For input string: "a"
First attempt: Using Cats State Monad
evalRPNExpression("1 3 + 10 -") // -6
evalRPNExpression("1 3 + 10 - +") // java.lang.UnsupportedOperationException: tail of empty list
evalRPNExpression("1 3 + 10 - a") // java.lang.NumberFormatException: For input string: "a"
First attempt: Using Cats State Monad
evalRPNExpression("1 3 + 10 -") // -6
evalRPNExpression("1 3 + 10 - +") // java.lang.UnsupportedOperationException: tail of empty list
evalRPNExpression("1 3 + 10 - a") // java.lang.NumberFormatException: For input string: "a"
Problems
Problems
• Doesn't handle errors appropriately
Second attempt: Cats State + Either
type Eff[A] = State[Stack, Either[String, A]]
val pop: Eff[Int] =
for {
stack <- State.get
x <- stack match {
case head :: tail => State.set(tail).as(Right(head))
case _ => State.pure[Stack, Either[String, Int]](Left("No operands left"))
}
} yield x
Second attempt: Cats State + Either
type Eff[A] = State[Stack, Either[String, A]]
val pop: Eff[Int] =
for {
stack <- State.get
x <- stack match {
case head :: tail => State.set(tail).as(Right(head))
case _ => State.pure[Stack, Either[String, Int]](Left("No operands left"))
}
} yield x
Second attempt: Cats State + Either
type Eff[A] = State[Stack, Either[String, A]]
val pop: Eff[Int] =
for {
stack <- State.get
x <- stack match {
case head :: tail => State.set(tail).as(Right(head))
case _ => State.pure[Stack, Either[String, Int]](Left("No operands left"))
}
} yield x
Second attempt: Cats State + Either
type Eff[A] = State[Stack, Either[String, A]]
val pop: Eff[Int] =
for {
stack <- State.get
x <- stack match {
case head :: tail => State.set(tail).as(Right(head))
case _ => State.pure[Stack, Either[String, Int]](Left("No operands left"))
}
} yield x
Second attempt: Cats State + Either
type Eff[A] = State[Stack, Either[String, A]]
val pop: Eff[Int] =
for {
stack <- State.get
x <- stack match {
case head :: tail => State.set(tail).as(Right(head))
case _ => State.pure[Stack, Either[String, Int]](Left("No operands left"))
}
} yield x
Second attempt:
Cats State + Either
def evalRPNExpression(elements: List[String]): Either[String, Int] = {
def processElements(elements: List[String]): Eff[Unit] =
elements match {
case head :: tail =>
for {
processed <- processElement(head)
result <- processed match {
case Left(error) => State.pure[Stack, Either[String, Unit]](Left(error))
case Right(_) => processElements(tail)
}
} yield result
case Nil => State.pure(Right(()))
}
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x =>
x.toIntOption match {
case Some(x) => push(x).map(Right(_))
case None => State.pure[Stack, Either[String, Unit]](Left(s"Invalid operand: $x"))
}
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
result <- (first, second) match {
case (Right(first), Right(second)) => push(operator(second, first))
case (Left(error), _) => State.pure[Stack, Either[String, Unit]](Left(error))
case (Right(_), Left(error)) => State.pure[Stack, Either[String, Unit]](Left(error))
}
} yield result
(for {
processed <- processElements(elements)
result <- processed match {
case Left(error) => State.pure[Stack, Either[String, Int]](Left(error))
case Right(_) => pop
}
} yield result).runA(Nil).value
}
Second attempt: Cats State + Either
evalRPNExpression("1 3 + 10 -") // Right(-6)
evalRPNExpression("1 3 + 10 - +") // Left(No operands left)
evalRPNExpression("1 3 + 10 a") // Left(Invalid operand: a)
Second attempt: Cats State + Either
evalRPNExpression("1 3 + 10 -") // Right(-6)
evalRPNExpression("1 3 + 10 - +") // Left(No operands left)
evalRPNExpression("1 3 + 10 a") // Left(Invalid operand: a)
Second attempt: Cats State + Either
evalRPNExpression("1 3 + 10 -") // Right(-6)
evalRPNExpression("1 3 + 10 - +") // Left(No operands left)
evalRPNExpression("1 3 + 10 a") // Left(Invalid operand: a)
Second attempt: Cats State + Either
evalRPNExpression("1 3 + 10 -") // Right(-6)
evalRPNExpression("1 3 + 10 - +") // Left(No operands left)
evalRPNExpression("1 3 + 10 a") // Left(Invalid operand: a)
Benefits
Benefits
• Errors are handled appropriately
Problems
Problems
• Too much boilerplate to handle errors
Problems
• Too much boilerplate to handle errors
• Poor type-inference
Third attempt: Monad Transformers
type Eff[A] = EitherT[State[Stack, *], String, A]
val pop: Eff[Int] =
for {
stack <- EitherT.liftF(State.get[Stack])
x <- stack match {
case head :: tail => EitherT.liftF[State[Stack, *], String, Int](State.set(tail).as(head))
case _ => EitherT.leftT[State[Stack, *], Int]("No operands left")
}
} yield x
Third attempt: Monad Transformers
type Eff[A] = EitherT[State[Stack, *], String, A]
val pop: Eff[Int] =
for {
stack <- EitherT.liftF(State.get[Stack])
x <- stack match {
case head :: tail => EitherT.liftF[State[Stack, *], String, Int](State.set(tail).as(head))
case _ => EitherT.leftT[State[Stack, *], Int]("No operands left")
}
} yield x
Third attempt: Monad Transformers
type Eff[A] = EitherT[State[Stack, *], String, A]
val pop: Eff[Int] =
for {
stack <- EitherT.liftF(State.get[Stack])
x <- stack match {
case head :: tail => EitherT.liftF[State[Stack, *], String, Int](State.set(tail).as(head))
case _ => EitherT.leftT[State[Stack, *], Int]("No operands left")
}
} yield x
Third attempt: Monad Transformers
type Eff[A] = EitherT[State[Stack, *], String, A]
val pop: Eff[Int] =
for {
stack <- EitherT.liftF(State.get[Stack])
x <- stack match {
case head :: tail => EitherT.liftF[State[Stack, *], String, Int](State.set(tail).as(head))
case _ => EitherT.leftT[State[Stack, *], Int]("No operands left")
}
} yield x
Third attempt: Monad Transformers
def evalRPNExpression(elements: List[String]): Either[String, Int] = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => EitherT.fromOption[State[Stack, *]](x.toIntOption, s"Invalid operand: $x").flatMap(push)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
result <- push(operator(second, first))
} yield result
(elements.traverse(processElement) *> pop).value.runA(Nil).value
}
Third attempt: Monad Transformers
def evalRPNExpression(elements: List[String]): Either[String, Int] = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => EitherT.fromOption[State[Stack, *]](x.toIntOption, s"Invalid operand: $x").flatMap(push)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
result <- push(operator(second, first))
} yield result
(elements.traverse(processElement) *> pop).value.runA(Nil).value
}
Benefits
Benefits
• Less boilerplate to handle errors
Problems
Problems
• There's still boilerplate to lift State into EitherT
Problems
• There's still boilerplate to lift State into EitherT
• Even worse type-inference
Problems
• There's still boilerplate to lift State into EitherT
• Even worse type-inference
• Performance
Problems
• There's still boilerplate to lift State into EitherT
• Even worse type-inference
• Performance
• Discoverability: If I want to handle additional effects
(Context, Logging), which Monad Transformers should I
add?
The Problem of Discoverability
Fourth attempt: ZIO 2
import zio._
type Stack = Ref[List[Int]]
type Eff[+A] = ZIO[Stack, String, A]
val pop: Eff[Int] =
for {
stackRef <- ZIO.service[Stack]
stack <- stackRef.get
x <- stack match {
case head :: tail => stackRef.set(tail).as(head)
case _ => ZIO.fail("No operands left")
}
} yield x
Fourth attempt: ZIO 2
import zio._
type Stack = Ref[List[Int]]
type Eff[+A] = ZIO[Stack, String, A]
val pop: Eff[Int] =
for {
stackRef <- ZIO.service[Stack]
stack <- stackRef.get
x <- stack match {
case head :: tail => stackRef.set(tail).as(head)
case _ => ZIO.fail("No operands left")
}
} yield x
Fourth attempt: ZIO 2
import zio._
type Stack = Ref[List[Int]]
type Eff[+A] = ZIO[Stack, String, A]
val pop: Eff[Int] =
for {
stackRef <- ZIO.service[Stack]
stack <- stackRef.get
x <- stack match {
case head :: tail => stackRef.set(tail).as(head)
case _ => ZIO.fail("No operands left")
}
} yield x
Fourth attempt: ZIO 2
import zio._
type Stack = Ref[List[Int]]
type Eff[+A] = ZIO[Stack, String, A]
val pop: Eff[Int] =
for {
stackRef <- ZIO.service[Stack]
stack <- stackRef.get
x <- stack match {
case head :: tail => stackRef.set(tail).as(head)
case _ => ZIO.fail("No operands left")
}
} yield x
Fourth attempt: ZIO 2
import zio._
type Stack = Ref[List[Int]]
type Eff[+A] = ZIO[Stack, String, A]
val pop: Eff[Int] =
for {
stackRef <- ZIO.service[Stack]
stack <- stackRef.get
x <- stack match {
case head :: tail => stackRef.set(tail).as(head)
case _ => ZIO.fail("No operands left")
}
} yield x
Fourth attempt: ZIO 2
import zio._
type Stack = Ref[List[Int]]
type Eff[+A] = ZIO[Stack, String, A]
val pop: Eff[Int] =
for {
stackRef <- ZIO.service[Stack]
stack <- stackRef.get
x <- stack match {
case head :: tail => stackRef.set(tail).as(head)
case _ => ZIO.fail("No operands left")
}
} yield x
Fourth attempt: ZIO 2
def evalRPNExpression(elements: List[String]): ZIO[ZEnv, IOException, Either[String, Int]] = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => ZIO.from(x.toIntOption).orElseFail(s"Invalid operand: $x").flatMap(push)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
result <- push(operator(second, first))
} yield result
(ZIO.foreachDiscard(elements)(processElement) *> pop).either
.provideCustomLayer(Ref.make(List.empty[Int]).toLayer)
}
Fourth attempt: ZIO 2
def evalRPNExpression(elements: List[String]): ZIO[ZEnv, IOException, Either[String, Int]] = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => ZIO.from(x.toIntOption).orElseFail(s"Invalid operand: $x").flatMap(push)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
result <- push(operator(second, first))
} yield result
(ZIO.foreachDiscard(elements)(processElement) *> pop).either
.provideCustomLayer(Ref.make(List.empty[Int]).toLayer)
}
Benefits of ZIO 2
Benefits of ZIO 2
• One monad to rule them all!
Benefits of ZIO 2
• One monad to rule them all!
• Superb type-inference
Benefits of ZIO 2
• One monad to rule them all!
• Superb type-inference
• Discoverable functionality: Just one data type with
concrete methods!
Comparing Monad Transformers and
ZIO 2
Problems
Problems
• We're killing a fly with a bazooka!
Problems
• We're killing a fly with a bazooka!
• We don't need asynchronicity to solve this problem
Problems
• We're killing a fly with a bazooka!
• We don't need asynchronicity to solve this problem
• Neither to interact with the outside world
Problems
• We're killing a fly with a bazooka!
• We don't need asynchronicity to solve this problem
• Neither to interact with the outside world
• Performance
Wouldn't it be great if we had a
data type that lets us scale back
on the power of ZIO 2, but with
the same high-performance,
type-inference, and ergonomics?
Enter ZPure!
Meet ZPure
ZPure[+W, -S1, +S2, -R, +E, +A]
A ZPure is a description of a Pure Computation that:
Meet ZPure
ZPure[+W, -S1, +S2, -R, +E, +A]
A ZPure is a description of a Pure Computation that:
• Requires an environment R
Meet ZPure
ZPure[+W, -S1, +S2, -R, +E, +A]
A ZPure is a description of a Pure Computation that:
• Requires an environment R
• An initial state S1
Meet ZPure
ZPure[+W, -S1, +S2, -R, +E, +A]
A ZPure is a description of a Pure Computation that:
• Requires an environment R
• An initial state S1
• Can fail with an error of type E
Meet ZPure
ZPure[+W, -S1, +S2, -R, +E, +A]
A ZPure is a description of a Pure Computation that:
• Requires an environment R
• An initial state S1
• Can fail with an error of type E
• Succeed with an updated state of type S2 and a value of type A, also
producing a log of type W
Mental Model of ZPure
(R, S1) => (Chunk[W], Either[E, (S2, A)])
So, ZPure models four effects that a computation can have besides
producing a value of type A:
Mental Model of ZPure
(R, S1) => (Chunk[W], Either[E, (S2, A)])
So, ZPure models four effects that a computation can have besides
producing a value of type A:
• Errors: Similar to Either
Mental Model of ZPure
(R, S1) => (Chunk[W], Either[E, (S2, A)])
So, ZPure models four effects that a computation can have besides
producing a value of type A:
• Errors: Similar to Either
• Context: Similar to Reader
Mental Model of ZPure
(R, S1) => (Chunk[W], Either[E, (S2, A)])
So, ZPure models four effects that a computation can have besides
producing a value of type A:
• Errors: Similar to Either
• Context: Similar to Reader
• State: Similar to State
Mental Model of ZPure
(R, S1) => (Chunk[W], Either[E, (S2, A)])
So, ZPure models four effects that a computation can have besides
producing a value of type A:
• Errors: Similar to Either
• Context: Similar to Reader
• State: Similar to State
• Logging: Similar to Writer
Type aliases of ZPure
type State[S, +A] = ZPure[Nothing, S, S, Any, Nothing, A]
type Reader[-R, +A] = ZPure[Nothing, Unit, Unit, R, Nothing, A]
type Writer[+W, +A] = ZPure[W, Unit, Unit, Any, Nothing, A]
Type aliases of ZPure
type EState[S, +E, +A] = ZPure[Nothing, S, S, Any, E, A]
type EReader[-R, +E, +A] = ZPure[Nothing, Unit, Unit, R, E, A]
type EWriter[+W, +E, +A] = ZPure[W, Unit, Unit, Any, E, A]
Final attempt: ZPure
import zio.prelude._
type Stack = List[Int]
type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A]
val pop: Eff[Int] =
for {
stack <- EState.get[Stack]
x <- stack match {
case head :: tail => EState.set(tail).as(head)
case _ => EState.fail("No operands left")
}
} yield x
Final attempt: ZPure
import zio.prelude._
type Stack = List[Int]
type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A]
val pop: Eff[Int] =
for {
stack <- EState.get[Stack]
x <- stack match {
case head :: tail => EState.set(tail).as(head)
case _ => EState.fail("No operands left")
}
} yield x
Final attempt: ZPure
import zio.prelude._
type Stack = List[Int]
type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A]
val pop: Eff[Int] =
for {
stack <- EState.get[Stack]
x <- stack match {
case head :: tail => EState.set(tail).as(head)
case _ => EState.fail("No operands left")
}
} yield x
Final attempt: ZPure
import zio.prelude._
type Stack = List[Int]
type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A]
val pop: Eff[Int] =
for {
stack <- EState.get[Stack]
x <- stack match {
case head :: tail => EState.set(tail).as(head)
case _ => EState.fail("No operands left")
}
} yield x
Final attempt: ZPure
import zio.prelude._
type Stack = List[Int]
type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A]
val pop: Eff[Int] =
for {
stack <- EState.get[Stack]
x <- stack match {
case head :: tail => EState.set(tail).as(head)
case _ => EState.fail("No operands left")
}
} yield x
Final attempt: ZPure
import zio.prelude._
type Stack = List[Int]
type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A]
val pop: Eff[Int] =
for {
stack <- EState.get[Stack]
x <- stack match {
case head :: tail => EState.set(tail).as(head)
case _ => EState.fail("No operands left")
}
} yield x
Final attempt: ZPure
def evalRPNExpression(elements: List[String]): Either[String, Int] = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => EState.fromOption(x.toIntOption).orElseFail(s"Invalid operand: $x").flatMap(push)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
result <- push(operator(second, first))
} yield result
// Here we use `forEach` from the ForEach typeclass
(elements.forEach(processElement) *> pop).provideState(Nil).runEither
}
Final attempt: ZPure
def evalRPNExpression(elements: List[String]): Either[String, Int] = {
def processElement(element: String): Eff[Unit] =
element match {
case "+" => processTopElements(_ + _)
case "-" => processTopElements(_ - _)
case "*" => processTopElements(_ * _)
case x => EState.fromOption(x.toIntOption).orElseFail(s"Invalid operand: $x").flatMap(push)
}
def processTopElements(operator: (Int, Int) => Int): Eff[Unit] =
for {
first <- pop
second <- pop
result <- push(operator(second, first))
} yield result
// Here we use `forEach` from the ForEach typeclass
(elements.forEach(processElement) *> pop).provideState(Nil).runEither
}
Comparing Monad Transformers and
ZPure
Comparing ZIO 2 and ZPure
Benefits of ZPure
Benefits of ZPure
• One monad to rule (almost) them all! (Except IO)
Benefits of ZPure
• One monad to rule (almost) them all! (Except IO)
• Superb type-inference
Benefits of ZPure
• One monad to rule (almost) them all! (Except IO)
• Superb type-inference
• Discoverable functionality: Just one data type with concrete
methods!
Benefits of ZPure
• One monad to rule (almost) them all! (Except IO)
• Superb type-inference
• Discoverable functionality: Just one data type with concrete
methods!
• ZIO idiomatic (Familiar and accessible method names)
Benefits of ZPure
• One monad to rule (almost) them all! (Except IO)
• Superb type-inference
• Discoverable functionality: Just one data type with concrete
methods!
• ZIO idiomatic (Familiar and accessible method names)
• Gradual adoption (use a little, then use more, then use more!)
Benefits of ZPure
• One monad to rule (almost) them all! (Except IO)
• Superb type-inference
• Discoverable functionality: Just one data type with concrete
methods!
• ZIO idiomatic (Familiar and accessible method names)
• Gradual adoption (use a little, then use more, then use more!)
• Performance!
Benchmarking State + Failure
Benchmarking State + Failure + Log
Summary
Summary
Summary
• We need a way to handle Context, State, Failures and
Logging in a Purely Functional way
Summary
• We need a way to handle Context, State, Failures and
Logging in a Purely Functional way
• There are several options, each one with its own
limitations
Summary
• We need a way to handle Context, State, Failures and
Logging in a Purely Functional way
• There are several options, each one with its own
limitations
• ZPure provides a highly-ergonomic, ZIO-idiomatic and
highly-performant solution to those limitations
Special thanks
Special thanks
• Ziverge for organizing ZIO World
Special thanks
• Ziverge for organizing ZIO World
• Scalac for sponsoring
Special thanks
• Ziverge for organizing ZIO World
• Scalac for sponsoring
• John De Goes for guidance and support
Contact me
@jorvasquez2301
jorge-vasquez-2301
jorge.vasquez@scalac.io

Más contenido relacionado

La actualidad más candente

Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackGaryCoady
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Philip Schwarz
 
Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Philip Schwarz
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongIf You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongMario Fusco
 
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 CatsPhilip Schwarz
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in ScalaHermann Hueck
 
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 2021Natan Silnitsky
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them AllJohn De Goes
 
Deep dive into Coroutines on JVM @ KotlinConf 2017
Deep dive into Coroutines on JVM @ KotlinConf 2017Deep dive into Coroutines on JVM @ KotlinConf 2017
Deep dive into Coroutines on JVM @ KotlinConf 2017Roman Elizarov
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadOliver Daff
 
ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022Alexander Ioffe
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsPhilip Schwarz
 
Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!Julien Truffaut
 
Introduction to SQLAlchemy ORM
Introduction to SQLAlchemy ORMIntroduction to SQLAlchemy ORM
Introduction to SQLAlchemy ORMJason Myers
 
Morel, a Functional Query Language
Morel, a Functional Query LanguageMorel, a Functional Query Language
Morel, a Functional Query LanguageJulian Hyde
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018John De Goes
 
Kotlin Collections
Kotlin CollectionsKotlin Collections
Kotlin CollectionsHalil Özcan
 

La actualidad más candente (20)

Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web Stack
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1
 
Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongIf You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are Wrong
 
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
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
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
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
 
Deep dive into Coroutines on JVM @ KotlinConf 2017
Deep dive into Coroutines on JVM @ KotlinConf 2017Deep dive into Coroutines on JVM @ KotlinConf 2017
Deep dive into Coroutines on JVM @ KotlinConf 2017
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
 
ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and Cats
 
Applicative style programming
Applicative style programmingApplicative style programming
Applicative style programming
 
JQuery selectors
JQuery selectors JQuery selectors
JQuery selectors
 
Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!
 
Introduction to SQLAlchemy ORM
Introduction to SQLAlchemy ORMIntroduction to SQLAlchemy ORM
Introduction to SQLAlchemy ORM
 
Morel, a Functional Query Language
Morel, a Functional Query LanguageMorel, a Functional Query Language
Morel, a Functional Query Language
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
Kotlin Collections
Kotlin CollectionsKotlin Collections
Kotlin Collections
 

Similar a A Prelude of Purity: Scaling Back ZIO

Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meetMario Fusco
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?Tomasz Wrobel
 
An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testingVincent Pradeilles
 
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...scalaconfjp
 
Learning Functional Programming Without Growing a Neckbeard
Learning Functional Programming Without Growing a NeckbeardLearning Functional Programming Without Growing a Neckbeard
Learning Functional Programming Without Growing a NeckbeardKelsey Gilmore-Innis
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala Knoldus Inc.
 
Swift Rocks #2: Going functional
Swift Rocks #2: Going functionalSwift Rocks #2: Going functional
Swift Rocks #2: Going functionalHackraft
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming iiPrashant Kalkar
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to SwiftGiordano Scalzo
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기진성 오
 

Similar a A Prelude of Purity: Scaling Back ZIO (20)

Scala for curious
Scala for curiousScala for curious
Scala for curious
 
Meet scala
Meet scalaMeet scala
Meet scala
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meet
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?
 
An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testing
 
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
 
Learning Functional Programming Without Growing a Neckbeard
Learning Functional Programming Without Growing a NeckbeardLearning Functional Programming Without Growing a Neckbeard
Learning Functional Programming Without Growing a Neckbeard
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala
 
Scala coated JVM
Scala coated JVMScala coated JVM
Scala coated JVM
 
An introduction to scala
An introduction to scalaAn introduction to scala
An introduction to scala
 
Swift Rocks #2: Going functional
Swift Rocks #2: Going functionalSwift Rocks #2: Going functional
Swift Rocks #2: Going functional
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming ii
 
Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to Swift
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기
 

Más de Jorge Vásquez

Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!Jorge Vásquez
 
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIOConsiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIOJorge Vásquez
 
ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021Jorge Vásquez
 
Exploring type level programming in Scala
Exploring type level programming in ScalaExploring type level programming in Scala
Exploring type level programming in ScalaJorge Vásquez
 
The Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at WorkThe Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at WorkJorge Vásquez
 
Introduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effectsIntroduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effectsJorge Vásquez
 

Más de Jorge Vásquez (6)

Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
 
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIOConsiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
 
ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021
 
Exploring type level programming in Scala
Exploring type level programming in ScalaExploring type level programming in Scala
Exploring type level programming in Scala
 
The Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at WorkThe Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at Work
 
Introduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effectsIntroduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effects
 

Último

SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationShrmpro
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastPapp Krisztián
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...masabamasaba
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfkalichargn70th171
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesVictorSzoltysek
 
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...Nitya salvi
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...masabamasaba
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnAmarnathKambale
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburgmasabamasaba
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 

Último (20)

SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions Presentation
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 

A Prelude of Purity: Scaling Back ZIO

  • 1. A Prelude of Purity Scaling Back ZIO ZIO World March 11th, 2022
  • 3. Background ZIO Prelude is a Scala-first take on functional abstractions
  • 4. Background ZIO Prelude is a Scala-first take on functional abstractions • Type classes to describe the ways different types are similar
  • 5. Background ZIO Prelude is a Scala-first take on functional abstractions • Type classes to describe the ways different types are similar • Smart Types for more precise data modelling
  • 6. Background ZIO Prelude is a Scala-first take on functional abstractions • Type classes to describe the ways different types are similar • Smart Types for more precise data modelling • Data types that complement the Scala standard library: ZPure
  • 7. Problem Using Purely Functional style, how can we write computations that:
  • 8. Problem Using Purely Functional style, how can we write computations that: • Are Stateful
  • 9. Problem Using Purely Functional style, how can we write computations that: • Are Stateful • Require a Context
  • 10. Problem Using Purely Functional style, how can we write computations that: • Are Stateful • Require a Context • Might Fail or Succeed with a value
  • 11. Problem Using Purely Functional style, how can we write computations that: • Are Stateful • Require a Context • Might Fail or Succeed with a value • Produce Logs
  • 13. Example "2 5 + 3 * 1 -" -> 20
  • 14. First attempt: Using Cats State Monad import cats.data._ import cats.implicits._ type Stack = List[Int] type Eff[A] = State[Stack, A] def push(x: Int): Eff[Unit] = State.modify(x :: _) val pop: Eff[Int] = for { stack <- State.get x <- State.set(stack.tail).as(stack.head) } yield x def getExprElements(expr: String): List[String] = expr.split(' ').toList
  • 15. First attempt: Using Cats State Monad import cats.data._ import cats.implicits._ type Stack = List[Int] type Eff[A] = State[Stack, A] def push(x: Int): Eff[Unit] = State.modify(x :: _) val pop: Eff[Int] = for { stack <- State.get x <- State.set(stack.tail).as(stack.head) } yield x def getExprElements(expr: String): List[String] = expr.split(' ').toList
  • 16. First attempt: Using Cats State Monad import cats.data._ import cats.implicits._ type Stack = List[Int] type Eff[A] = State[Stack, A] def push(x: Int): Eff[Unit] = State.modify(x :: _) val pop: Eff[Int] = for { stack <- State.get x <- State.set(stack.tail).as(stack.head) } yield x def getExprElements(expr: String): List[String] = expr.split(' ').toList
  • 17. First attempt: Using Cats State Monad import cats.data._ import cats.implicits._ type Stack = List[Int] type Eff[A] = State[Stack, A] def push(x: Int): Eff[Unit] = State.modify(x :: _) val pop: Eff[Int] = for { stack <- State.get x <- State.set(stack.tail).as(stack.head) } yield x def getExprElements(expr: String): List[String] = expr.split(' ').toList
  • 18. First attempt: Using Cats State Monad import cats.data._ import cats.implicits._ type Stack = List[Int] type Eff[A] = State[Stack, A] def push(x: Int): Eff[Unit] = State.modify(x :: _) val pop: Eff[Int] = for { stack <- State.get x <- State.set(stack.tail).as(stack.head) } yield x def getExprElements(expr: String): List[String] = expr.split(' ').toList
  • 19. First attempt: Using Cats State Monad import cats.data._ import cats.implicits._ type Stack = List[Int] type Eff[A] = State[Stack, A] def push(x: Int): Eff[Unit] = State.modify(x :: _) val pop: Eff[Int] = for { stack <- State.get x <- State.set(stack.tail).as(stack.head) } yield x def getExprElements(expr: String): List[String] = expr.split(' ').toList
  • 20. First attempt: Using Cats State Monad import cats.data._ import cats.implicits._ type Stack = List[Int] type Eff[A] = State[Stack, A] def push(x: Int): Eff[Unit] = State.modify(x :: _) val pop: Eff[Int] = for { stack <- State.get x <- State.set(stack.tail).as(stack.head) } yield x def getExprElements(expr: String): List[String] = expr.split(' ').toList
  • 21. First attempt: Using Cats State Monad def evalRPNExpression(elements: List[String]): Int = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => push(x.toInt) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop _ <- push(operator(second, first)) } yield () (elements.traverse(processElement) *> pop).runA(Nil).value }
  • 22. First attempt: Using Cats State Monad def evalRPNExpression(elements: List[String]): Int = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => push(x.toInt) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop _ <- push(operator(second, first)) } yield () (elements.traverse(processElement) *> pop).runA(Nil).value }
  • 23. First attempt: Using Cats State Monad def evalRPNExpression(elements: List[String]): Int = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => push(x.toInt) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop _ <- push(operator(second, first)) } yield () (elements.traverse(processElement) *> pop).runA(Nil).value }
  • 24. First attempt: Using Cats State Monad def evalRPNExpression(elements: List[String]): Int = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => push(x.toInt) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop _ <- push(operator(second, first)) } yield () (elements.traverse(processElement) *> pop).runA(Nil).value }
  • 25. First attempt: Using Cats State Monad evalRPNExpression("1 3 + 10 -") // -6 evalRPNExpression("1 3 + 10 - +") // java.lang.UnsupportedOperationException: tail of empty list evalRPNExpression("1 3 + 10 - a") // java.lang.NumberFormatException: For input string: "a"
  • 26. First attempt: Using Cats State Monad evalRPNExpression("1 3 + 10 -") // -6 evalRPNExpression("1 3 + 10 - +") // java.lang.UnsupportedOperationException: tail of empty list evalRPNExpression("1 3 + 10 - a") // java.lang.NumberFormatException: For input string: "a"
  • 27. First attempt: Using Cats State Monad evalRPNExpression("1 3 + 10 -") // -6 evalRPNExpression("1 3 + 10 - +") // java.lang.UnsupportedOperationException: tail of empty list evalRPNExpression("1 3 + 10 - a") // java.lang.NumberFormatException: For input string: "a"
  • 28. First attempt: Using Cats State Monad evalRPNExpression("1 3 + 10 -") // -6 evalRPNExpression("1 3 + 10 - +") // java.lang.UnsupportedOperationException: tail of empty list evalRPNExpression("1 3 + 10 - a") // java.lang.NumberFormatException: For input string: "a"
  • 29.
  • 31. Problems • Doesn't handle errors appropriately
  • 32. Second attempt: Cats State + Either type Eff[A] = State[Stack, Either[String, A]] val pop: Eff[Int] = for { stack <- State.get x <- stack match { case head :: tail => State.set(tail).as(Right(head)) case _ => State.pure[Stack, Either[String, Int]](Left("No operands left")) } } yield x
  • 33. Second attempt: Cats State + Either type Eff[A] = State[Stack, Either[String, A]] val pop: Eff[Int] = for { stack <- State.get x <- stack match { case head :: tail => State.set(tail).as(Right(head)) case _ => State.pure[Stack, Either[String, Int]](Left("No operands left")) } } yield x
  • 34. Second attempt: Cats State + Either type Eff[A] = State[Stack, Either[String, A]] val pop: Eff[Int] = for { stack <- State.get x <- stack match { case head :: tail => State.set(tail).as(Right(head)) case _ => State.pure[Stack, Either[String, Int]](Left("No operands left")) } } yield x
  • 35. Second attempt: Cats State + Either type Eff[A] = State[Stack, Either[String, A]] val pop: Eff[Int] = for { stack <- State.get x <- stack match { case head :: tail => State.set(tail).as(Right(head)) case _ => State.pure[Stack, Either[String, Int]](Left("No operands left")) } } yield x
  • 36. Second attempt: Cats State + Either type Eff[A] = State[Stack, Either[String, A]] val pop: Eff[Int] = for { stack <- State.get x <- stack match { case head :: tail => State.set(tail).as(Right(head)) case _ => State.pure[Stack, Either[String, Int]](Left("No operands left")) } } yield x
  • 37. Second attempt: Cats State + Either def evalRPNExpression(elements: List[String]): Either[String, Int] = { def processElements(elements: List[String]): Eff[Unit] = elements match { case head :: tail => for { processed <- processElement(head) result <- processed match { case Left(error) => State.pure[Stack, Either[String, Unit]](Left(error)) case Right(_) => processElements(tail) } } yield result case Nil => State.pure(Right(())) } def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => x.toIntOption match { case Some(x) => push(x).map(Right(_)) case None => State.pure[Stack, Either[String, Unit]](Left(s"Invalid operand: $x")) } } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop result <- (first, second) match { case (Right(first), Right(second)) => push(operator(second, first)) case (Left(error), _) => State.pure[Stack, Either[String, Unit]](Left(error)) case (Right(_), Left(error)) => State.pure[Stack, Either[String, Unit]](Left(error)) } } yield result (for { processed <- processElements(elements) result <- processed match { case Left(error) => State.pure[Stack, Either[String, Int]](Left(error)) case Right(_) => pop } } yield result).runA(Nil).value }
  • 38. Second attempt: Cats State + Either evalRPNExpression("1 3 + 10 -") // Right(-6) evalRPNExpression("1 3 + 10 - +") // Left(No operands left) evalRPNExpression("1 3 + 10 a") // Left(Invalid operand: a)
  • 39. Second attempt: Cats State + Either evalRPNExpression("1 3 + 10 -") // Right(-6) evalRPNExpression("1 3 + 10 - +") // Left(No operands left) evalRPNExpression("1 3 + 10 a") // Left(Invalid operand: a)
  • 40. Second attempt: Cats State + Either evalRPNExpression("1 3 + 10 -") // Right(-6) evalRPNExpression("1 3 + 10 - +") // Left(No operands left) evalRPNExpression("1 3 + 10 a") // Left(Invalid operand: a)
  • 41. Second attempt: Cats State + Either evalRPNExpression("1 3 + 10 -") // Right(-6) evalRPNExpression("1 3 + 10 - +") // Left(No operands left) evalRPNExpression("1 3 + 10 a") // Left(Invalid operand: a)
  • 43. Benefits • Errors are handled appropriately
  • 45. Problems • Too much boilerplate to handle errors
  • 46. Problems • Too much boilerplate to handle errors • Poor type-inference
  • 47.
  • 48. Third attempt: Monad Transformers type Eff[A] = EitherT[State[Stack, *], String, A] val pop: Eff[Int] = for { stack <- EitherT.liftF(State.get[Stack]) x <- stack match { case head :: tail => EitherT.liftF[State[Stack, *], String, Int](State.set(tail).as(head)) case _ => EitherT.leftT[State[Stack, *], Int]("No operands left") } } yield x
  • 49. Third attempt: Monad Transformers type Eff[A] = EitherT[State[Stack, *], String, A] val pop: Eff[Int] = for { stack <- EitherT.liftF(State.get[Stack]) x <- stack match { case head :: tail => EitherT.liftF[State[Stack, *], String, Int](State.set(tail).as(head)) case _ => EitherT.leftT[State[Stack, *], Int]("No operands left") } } yield x
  • 50. Third attempt: Monad Transformers type Eff[A] = EitherT[State[Stack, *], String, A] val pop: Eff[Int] = for { stack <- EitherT.liftF(State.get[Stack]) x <- stack match { case head :: tail => EitherT.liftF[State[Stack, *], String, Int](State.set(tail).as(head)) case _ => EitherT.leftT[State[Stack, *], Int]("No operands left") } } yield x
  • 51. Third attempt: Monad Transformers type Eff[A] = EitherT[State[Stack, *], String, A] val pop: Eff[Int] = for { stack <- EitherT.liftF(State.get[Stack]) x <- stack match { case head :: tail => EitherT.liftF[State[Stack, *], String, Int](State.set(tail).as(head)) case _ => EitherT.leftT[State[Stack, *], Int]("No operands left") } } yield x
  • 52. Third attempt: Monad Transformers def evalRPNExpression(elements: List[String]): Either[String, Int] = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => EitherT.fromOption[State[Stack, *]](x.toIntOption, s"Invalid operand: $x").flatMap(push) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop result <- push(operator(second, first)) } yield result (elements.traverse(processElement) *> pop).value.runA(Nil).value }
  • 53. Third attempt: Monad Transformers def evalRPNExpression(elements: List[String]): Either[String, Int] = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => EitherT.fromOption[State[Stack, *]](x.toIntOption, s"Invalid operand: $x").flatMap(push) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop result <- push(operator(second, first)) } yield result (elements.traverse(processElement) *> pop).value.runA(Nil).value }
  • 55. Benefits • Less boilerplate to handle errors
  • 57. Problems • There's still boilerplate to lift State into EitherT
  • 58. Problems • There's still boilerplate to lift State into EitherT • Even worse type-inference
  • 59. Problems • There's still boilerplate to lift State into EitherT • Even worse type-inference • Performance
  • 60. Problems • There's still boilerplate to lift State into EitherT • Even worse type-inference • Performance • Discoverability: If I want to handle additional effects (Context, Logging), which Monad Transformers should I add?
  • 61. The Problem of Discoverability
  • 62.
  • 63. Fourth attempt: ZIO 2 import zio._ type Stack = Ref[List[Int]] type Eff[+A] = ZIO[Stack, String, A] val pop: Eff[Int] = for { stackRef <- ZIO.service[Stack] stack <- stackRef.get x <- stack match { case head :: tail => stackRef.set(tail).as(head) case _ => ZIO.fail("No operands left") } } yield x
  • 64. Fourth attempt: ZIO 2 import zio._ type Stack = Ref[List[Int]] type Eff[+A] = ZIO[Stack, String, A] val pop: Eff[Int] = for { stackRef <- ZIO.service[Stack] stack <- stackRef.get x <- stack match { case head :: tail => stackRef.set(tail).as(head) case _ => ZIO.fail("No operands left") } } yield x
  • 65. Fourth attempt: ZIO 2 import zio._ type Stack = Ref[List[Int]] type Eff[+A] = ZIO[Stack, String, A] val pop: Eff[Int] = for { stackRef <- ZIO.service[Stack] stack <- stackRef.get x <- stack match { case head :: tail => stackRef.set(tail).as(head) case _ => ZIO.fail("No operands left") } } yield x
  • 66. Fourth attempt: ZIO 2 import zio._ type Stack = Ref[List[Int]] type Eff[+A] = ZIO[Stack, String, A] val pop: Eff[Int] = for { stackRef <- ZIO.service[Stack] stack <- stackRef.get x <- stack match { case head :: tail => stackRef.set(tail).as(head) case _ => ZIO.fail("No operands left") } } yield x
  • 67. Fourth attempt: ZIO 2 import zio._ type Stack = Ref[List[Int]] type Eff[+A] = ZIO[Stack, String, A] val pop: Eff[Int] = for { stackRef <- ZIO.service[Stack] stack <- stackRef.get x <- stack match { case head :: tail => stackRef.set(tail).as(head) case _ => ZIO.fail("No operands left") } } yield x
  • 68. Fourth attempt: ZIO 2 import zio._ type Stack = Ref[List[Int]] type Eff[+A] = ZIO[Stack, String, A] val pop: Eff[Int] = for { stackRef <- ZIO.service[Stack] stack <- stackRef.get x <- stack match { case head :: tail => stackRef.set(tail).as(head) case _ => ZIO.fail("No operands left") } } yield x
  • 69. Fourth attempt: ZIO 2 def evalRPNExpression(elements: List[String]): ZIO[ZEnv, IOException, Either[String, Int]] = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => ZIO.from(x.toIntOption).orElseFail(s"Invalid operand: $x").flatMap(push) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop result <- push(operator(second, first)) } yield result (ZIO.foreachDiscard(elements)(processElement) *> pop).either .provideCustomLayer(Ref.make(List.empty[Int]).toLayer) }
  • 70. Fourth attempt: ZIO 2 def evalRPNExpression(elements: List[String]): ZIO[ZEnv, IOException, Either[String, Int]] = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => ZIO.from(x.toIntOption).orElseFail(s"Invalid operand: $x").flatMap(push) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop result <- push(operator(second, first)) } yield result (ZIO.foreachDiscard(elements)(processElement) *> pop).either .provideCustomLayer(Ref.make(List.empty[Int]).toLayer) }
  • 72. Benefits of ZIO 2 • One monad to rule them all!
  • 73. Benefits of ZIO 2 • One monad to rule them all! • Superb type-inference
  • 74. Benefits of ZIO 2 • One monad to rule them all! • Superb type-inference • Discoverable functionality: Just one data type with concrete methods!
  • 77. Problems • We're killing a fly with a bazooka!
  • 78. Problems • We're killing a fly with a bazooka! • We don't need asynchronicity to solve this problem
  • 79. Problems • We're killing a fly with a bazooka! • We don't need asynchronicity to solve this problem • Neither to interact with the outside world
  • 80. Problems • We're killing a fly with a bazooka! • We don't need asynchronicity to solve this problem • Neither to interact with the outside world • Performance
  • 81.
  • 82. Wouldn't it be great if we had a data type that lets us scale back on the power of ZIO 2, but with the same high-performance, type-inference, and ergonomics?
  • 84. Meet ZPure ZPure[+W, -S1, +S2, -R, +E, +A] A ZPure is a description of a Pure Computation that:
  • 85. Meet ZPure ZPure[+W, -S1, +S2, -R, +E, +A] A ZPure is a description of a Pure Computation that: • Requires an environment R
  • 86. Meet ZPure ZPure[+W, -S1, +S2, -R, +E, +A] A ZPure is a description of a Pure Computation that: • Requires an environment R • An initial state S1
  • 87. Meet ZPure ZPure[+W, -S1, +S2, -R, +E, +A] A ZPure is a description of a Pure Computation that: • Requires an environment R • An initial state S1 • Can fail with an error of type E
  • 88. Meet ZPure ZPure[+W, -S1, +S2, -R, +E, +A] A ZPure is a description of a Pure Computation that: • Requires an environment R • An initial state S1 • Can fail with an error of type E • Succeed with an updated state of type S2 and a value of type A, also producing a log of type W
  • 89. Mental Model of ZPure (R, S1) => (Chunk[W], Either[E, (S2, A)]) So, ZPure models four effects that a computation can have besides producing a value of type A:
  • 90. Mental Model of ZPure (R, S1) => (Chunk[W], Either[E, (S2, A)]) So, ZPure models four effects that a computation can have besides producing a value of type A: • Errors: Similar to Either
  • 91. Mental Model of ZPure (R, S1) => (Chunk[W], Either[E, (S2, A)]) So, ZPure models four effects that a computation can have besides producing a value of type A: • Errors: Similar to Either • Context: Similar to Reader
  • 92. Mental Model of ZPure (R, S1) => (Chunk[W], Either[E, (S2, A)]) So, ZPure models four effects that a computation can have besides producing a value of type A: • Errors: Similar to Either • Context: Similar to Reader • State: Similar to State
  • 93. Mental Model of ZPure (R, S1) => (Chunk[W], Either[E, (S2, A)]) So, ZPure models four effects that a computation can have besides producing a value of type A: • Errors: Similar to Either • Context: Similar to Reader • State: Similar to State • Logging: Similar to Writer
  • 94. Type aliases of ZPure type State[S, +A] = ZPure[Nothing, S, S, Any, Nothing, A] type Reader[-R, +A] = ZPure[Nothing, Unit, Unit, R, Nothing, A] type Writer[+W, +A] = ZPure[W, Unit, Unit, Any, Nothing, A]
  • 95. Type aliases of ZPure type EState[S, +E, +A] = ZPure[Nothing, S, S, Any, E, A] type EReader[-R, +E, +A] = ZPure[Nothing, Unit, Unit, R, E, A] type EWriter[+W, +E, +A] = ZPure[W, Unit, Unit, Any, E, A]
  • 96. Final attempt: ZPure import zio.prelude._ type Stack = List[Int] type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A] val pop: Eff[Int] = for { stack <- EState.get[Stack] x <- stack match { case head :: tail => EState.set(tail).as(head) case _ => EState.fail("No operands left") } } yield x
  • 97. Final attempt: ZPure import zio.prelude._ type Stack = List[Int] type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A] val pop: Eff[Int] = for { stack <- EState.get[Stack] x <- stack match { case head :: tail => EState.set(tail).as(head) case _ => EState.fail("No operands left") } } yield x
  • 98. Final attempt: ZPure import zio.prelude._ type Stack = List[Int] type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A] val pop: Eff[Int] = for { stack <- EState.get[Stack] x <- stack match { case head :: tail => EState.set(tail).as(head) case _ => EState.fail("No operands left") } } yield x
  • 99. Final attempt: ZPure import zio.prelude._ type Stack = List[Int] type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A] val pop: Eff[Int] = for { stack <- EState.get[Stack] x <- stack match { case head :: tail => EState.set(tail).as(head) case _ => EState.fail("No operands left") } } yield x
  • 100. Final attempt: ZPure import zio.prelude._ type Stack = List[Int] type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A] val pop: Eff[Int] = for { stack <- EState.get[Stack] x <- stack match { case head :: tail => EState.set(tail).as(head) case _ => EState.fail("No operands left") } } yield x
  • 101. Final attempt: ZPure import zio.prelude._ type Stack = List[Int] type Eff[+A] = EState[Stack, String, A] // ZPure[Nothing, Stack, Stack, Any, String, A] val pop: Eff[Int] = for { stack <- EState.get[Stack] x <- stack match { case head :: tail => EState.set(tail).as(head) case _ => EState.fail("No operands left") } } yield x
  • 102. Final attempt: ZPure def evalRPNExpression(elements: List[String]): Either[String, Int] = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => EState.fromOption(x.toIntOption).orElseFail(s"Invalid operand: $x").flatMap(push) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop result <- push(operator(second, first)) } yield result // Here we use `forEach` from the ForEach typeclass (elements.forEach(processElement) *> pop).provideState(Nil).runEither }
  • 103. Final attempt: ZPure def evalRPNExpression(elements: List[String]): Either[String, Int] = { def processElement(element: String): Eff[Unit] = element match { case "+" => processTopElements(_ + _) case "-" => processTopElements(_ - _) case "*" => processTopElements(_ * _) case x => EState.fromOption(x.toIntOption).orElseFail(s"Invalid operand: $x").flatMap(push) } def processTopElements(operator: (Int, Int) => Int): Eff[Unit] = for { first <- pop second <- pop result <- push(operator(second, first)) } yield result // Here we use `forEach` from the ForEach typeclass (elements.forEach(processElement) *> pop).provideState(Nil).runEither }
  • 105. Comparing ZIO 2 and ZPure
  • 107. Benefits of ZPure • One monad to rule (almost) them all! (Except IO)
  • 108. Benefits of ZPure • One monad to rule (almost) them all! (Except IO) • Superb type-inference
  • 109. Benefits of ZPure • One monad to rule (almost) them all! (Except IO) • Superb type-inference • Discoverable functionality: Just one data type with concrete methods!
  • 110. Benefits of ZPure • One monad to rule (almost) them all! (Except IO) • Superb type-inference • Discoverable functionality: Just one data type with concrete methods! • ZIO idiomatic (Familiar and accessible method names)
  • 111. Benefits of ZPure • One monad to rule (almost) them all! (Except IO) • Superb type-inference • Discoverable functionality: Just one data type with concrete methods! • ZIO idiomatic (Familiar and accessible method names) • Gradual adoption (use a little, then use more, then use more!)
  • 112. Benefits of ZPure • One monad to rule (almost) them all! (Except IO) • Superb type-inference • Discoverable functionality: Just one data type with concrete methods! • ZIO idiomatic (Familiar and accessible method names) • Gradual adoption (use a little, then use more, then use more!) • Performance!
  • 113.
  • 115.
  • 116. Benchmarking State + Failure + Log
  • 117.
  • 120. Summary • We need a way to handle Context, State, Failures and Logging in a Purely Functional way
  • 121. Summary • We need a way to handle Context, State, Failures and Logging in a Purely Functional way • There are several options, each one with its own limitations
  • 122. Summary • We need a way to handle Context, State, Failures and Logging in a Purely Functional way • There are several options, each one with its own limitations • ZPure provides a highly-ergonomic, ZIO-idiomatic and highly-performant solution to those limitations
  • 124. Special thanks • Ziverge for organizing ZIO World
  • 125. Special thanks • Ziverge for organizing ZIO World • Scalac for sponsoring
  • 126. Special thanks • Ziverge for organizing ZIO World • Scalac for sponsoring • John De Goes for guidance and support