SlideShare a Scribd company logo
The Death of Tagless Final
John A. De Goes — @jdegoes
Wiem Zine Elabidine — @wiemzin
Amsterdam.scala & FP AMS
Xebia, Amsterdam
March 2, 2019
The Death of Final Tagless
SPECIAL THANKS
Amsterdam.scala
FP AMS
First-Class Effects Tagless Final
ZIO Environment Wrap Up
FIRST-CLASS EFFECTS
PROGRAMS AS STATEMENTS
def main: Unit = {
println("Good morning, what is your name?")
val name = readLine()
println(s"Good to meet you, $name!")
}
PROGRAMS AS STATEMENTS
val example: Double = …
val value = example
val values = List.fill(10)(value)
val plusone = value => value + 1
PROGRAMS AS STATEMENTS
def main: Unit = …
val program = main
val programs = List.fill(10)(main)
val retried = program =>
program.retried(2.times)
BENEFITS OF VALUES
Abstraction
Friendly
Refactor
Friendly
Test
Friendly
BENEFITS OF VALUES
def main: Unit = {
println("Good morning, what is your name?")
val name = readLine()
println(s"Good to meet you, $name!")
}
Procedural
Effects
Functional
Effects
Functional effects are immutable data structures
that merely describe sequences of operations.
At the end of the world, the data structure has to
be impurely “interpreted” to real world effects.
println("Hello World")
Procedural Effects “Do”
case class PrintLine(text: String)
Functional Effects “Describe”
PROGRAMS AS VALUES
IO[A]
A description of an effect that when unsafely
interpreted, will succeed with a value of type A
PROGRAMS AS VALUES
class IO[+A](val unsafeInterpret: () => A) { s =>
def map[B](f: A => B) = flatMap(f.andThen(IO.effect(_)))
def flatMap[B](f: A => IO[B]): IO[B] =
IO.effect(f(s.unsafeInterpret()).unsafeInterpret())
}
object IO {
def effect[A](eff: => A) = new IO(() => eff)
}
PROGRAMS AS VALUES
def putStrLn(line: String): IO[Unit] =
IO.effect(println(line))
val getStrLn: IO[String] =
IO.effect(scala.io.StdIn.readLine())
PROGRAMS AS VALUES
val main = for {
_ <- putStrLn("Good morning, " +
"what is your name?")
name <- getStrLn
_ <- putStrLn(s"Good to meet you, $name!")
} yield ()
PROGRAMS AS VALUES
val main = …
val program = main
val programs = List.fill(10)(main)
val retried = program =>
program.retried(2.times)
PROGRAMS AS VALUES
def loadTest(url: String, n: Int) =
val policy = Schedule.recurs(10).jittered
val worker = client.get(url).retry(policy)
val workers = List.fill(n)(worker)
IO.collectAllPar(workers)
}
PROGRAMS AS VALUES
def loadTest(url: String, n: Int) =
val policy = Schedule.recurs(10).jittered
val worker = client.get(url).retry(policy)
val workers = List.fill(n)(worker)
IO.collectAllPar(workers)
}
PROGRAMS AS VALUES
def loadTest(url: String, n: Int) =
val policy = Schedule.recurs(10).jittered
val worker = client.get(url).retry(policy)
val workers = List.fill(n)(worker)
IO.collectAllPar(workers)
}
PROGRAMS AS VALUES
def loadTest(url: String, n: Int) =
val policy = Schedule.recurs(10).jittered
val worker = client.get(url).retry(policy)
val workers = List.fill(n)(worker)
IO.collectAllPar(workers)
}
Functional Effects
Abstraction
Friendly
Refactor
Friendly
Test
Friendly
But!
Functional Effects
Abstraction
Friendly
Refactor
Friendly
Test
Friendly
PROGRAMS AS (OPAQUE) VALUES
def putStrLn(line: String): IO[Unit] =
IO.effect(println(line))
val getStrLn: IO[String] =
IO.effect(scala.io.StdIn.readLine())
TAGLESS-FINAL
BACK TO BASICS: JAVA 101
interface Console {
void println(String line);
String readLine();
}
BACK TO BASICS: JAVA 101
public void program(Console console) {
console.println("Good morning, " +
"what is your name?");
String name = console.readLine();
console.println(s"Good to meet you, $name!");
}
BACK TO BASICS: JAVA 101
class LiveConsole implements Console { … }
class TestConsole implements Console { … }
// In production:
program(new LiveConsole());
// In tests:
program(new TestConsole());
Purely!
IO INTERFACES
trait Console {
def println(line: String): IO[Unit]
val readLine: IO[String]
}
PURE PROGRAM WITH INTERFACE
def program(c: Console) = for {
_ <- c.println("Good morning, " +
"what is your name?")
name <- c.readLine
_ <- c.println(s"Good to meet you, $name!")
} yield ()
POLYMORPHIC EFFECT
trait Console[IO[_]] {
def println(String line): IO[Unit]
val readLine: IO[String]
}
MAKING IT EVEN MORE OBSCURE
trait Console[F[_]] {
def println(String line): F[Unit]
val readLine: F[String]
}
THE GLORIOUS TAGLESS-FINAL TYPE CLASS
trait Console[F[_]] {
def println(String line): F[Unit]
val readLine: F[String]
}
object Console {
def apply[F[_]](implicit F: Console[F]) = F
}
EFFECT-POLYMORPHIC PROGRAMS
def program[F[_]: Console: Monad] = for {
_ <- Console[F].println(
"Good morning, " +
"what is your name?")
name <- Console[F].readLine
_ <- Console[F].println(
s"Good to meet you, $name!")
} yield ()
TEST VS PRODUCTION EFFECTS
case class TestIO[A](...)
implicit val TestConsole = new Console[TestIO] { … }
implicit val LiveConsole = new Console[IO] { … }
// In production:
program[IO] : IO[Unit]
// In tests:
program[TestIO] : TestIO[Unit]
Functional Effects
Abstraction
Friendly
Refactor
Friendly
Test
Friendly
Functional Effects
Abstraction
Friendly
Refactor
Friendly
Test
Friendly
TAGLESS-FINAL IS EASY!
Functional Effects
IO[A]
TAGLESS-FINAL IS PRETTY EASY!
Functional Effects Parametric Polymorphism
F
TAGLESS-FINAL IS EASY-ISH!
Functional Effects Parametric Polymorphism
F[_]
Higher-Kinded Types
TAGLESS-FINAL ISN’T THAT HARD!
Functional Effects Parametric Polymorphism
trait Console[F[_]] { … }
Higher-Kinded Types Type Classes
TAGLESS-FINAL COULD BE MUCH HARDER!
Functional Effects Parametric Polymorphism
implicit val TestConsole = new Console[TestIO] { … }
Higher-Kinded Types Type Classes
Type Class Instances
TAGLESS-FINAL IS SORT OF HARD!
Functional Effects Parametric Polymorphism
new Console[({type F[A] = State[TestData, A]})#F] { … }
Higher-Kinded Types Type Classes
Type Class Instances Partial Type Application
OMG, TAGLESS-FINAL IS THE WORST!!!
Functional Effects Parametric Polymorphism
def program[F[_]: Monad: Console: Database] = ...
Higher-Kinded Types Type Classes
Type Class Instances Partial Type Application Monad Hierarchy
TAGLESS-FINAL IS MANY THINGS
EASY IS NOT ONE OF THEM
☠ TYPE CLASS ABUSE ☠
THE DARK SIDE OF TAGLESS-FINAL
REAL TYPE CLASS
Defines common
structure across types
through lawful
operations, enabling
abstraction.
FAKE TYPE CLASS
Defines a common
interface across types
through ad hoc
polymorphism,
enabling testing.
☠ TYPE CLASS ABUSE ☠
THE DARK SIDE OF TAGLESS-FINAL
REAL TYPE CLASS
Defines common
structure across types
through lawful
operations, enabling
abstraction.
FAKE TYPE CLASS
Defines a common
interface across types
through ad hoc
polymorphism,
enabling testing.
☠ BIG BANG INTRODUCTION ☠
THE DARK SIDE OF TAGLESS-FINAL
def program[F[_]: Console: Monad] = for {
_ <- Console[F].println(
"Good morning, " +
"what is your name?")
name <- Console[F].readLine
_ <- Console[F].println(
s"Good to meet you, $name!")
} yield ()
☠ TEDIOUS REPETITION ☠
THE DARK SIDE OF TAGLESS-FINAL
def genFeed[F[_]: Monad:
Logging: UserDatabase:
ProfileDatabase: RedisCache:
GeoIPService: AuthService:
SessionManager: Localization:
Config: EventQueue: Concurrent:
Async: MetricsManager]: F[Feed] = ???
☠ STUBBORN REPETITION ☠
THE DARK SIDE OF TAGLESS-FINAL
def genFeed[F[_]: Everything]: F[Feed] =
???
☠ COMPLETELY NON-INFERABLE ☠
THE DARK SIDE OF TAGLESS-FINAL
def genFeed = ???
☠ FAKE PARAMETRIC GUARANTEES ☠
def innocent[F[_]: Monad]: F[Unit] = {
def effect[A](a: => A): F[A] =
Monad[F].point(()).map(_ => a)
println("What guarantees?")
effect(System.exit(42))
}
THE DARK SIDE OF TAGLESS-FINAL
THE DEATH OF TAGLESS-FINAL
THE DEATH OF TAGLESS-FINAL
ZIO ENVIRONMENT
ZIO is a zero-dependency Scala library for
asynchronous and concurrent programming.
High-Performance
Γ
Type-Safe
ǁ
Concurrent
⇅
Asynchronous Resource-Safe
✓
Testable
Resilient
λ
Functional
BACK TO BASICS
trait Console {
def println(line: String): IO[Unit]
val readLine: IO[String]
}
BACK TO BASICS
def program(c: Console) = for {
_ <- c.println("Good morning, " +
"What is your name?")
name <- c.readLine
_ <- c.println(s"Good to meet you, $name!")
} yield ()
BACK TO BASICS
def program(c: Console, p: Persistence) = for {
_ <- c.println("Good morning, " +
"what is your name?")
name <- c.readLine
_ <- p.savePreferences(name)
_ <- c.println(s"Good to meet you, $name!")
} yield ()
BACK TO BASICS
def program(s1: Service1, s2: Service2,
s3: Service3, … sn: ServiceN) =
for {
a <- foo(s1, s9, s3)("localhost", 42)
b <- bar(sn, s19, s3)(a, 1024)
...
} yield z
BACK TO BASICS
def program(s1: Service1, s2: Service2,
s3: Service3, … sn: ServiceN) =
for {
a <- foo(s1, s9, s3)("localhost", 42)
b <- bar(sn, s19, s3)(a, 1024)
...
} yield z
THE MODULE PATTERN
trait HasConsole {
def console: HasConsole.Service
}
object HasConsole {
trait Service {
def println(line: String): IO[Unit]
val readLine: IO[String]
}
}
THE MODULE PATTERN
trait HasConsole {
def console: HasConsole.Service
}
object HasConsole {
trait Service {
def println(line: String): IO[Unit]
val readLine: IO[String]
}
}
THE MODULE PATTERN
def program(s: HasConsole with HasPersistence) =
for {
_ <- s.console.println("What is your name?")
name <- s.console.readLine
_ <- s.persistence.savePreferences(name)
_ <- s.console.println(s"Good to meet” +
” you, $name!")
} yield ()
THE MODULE PATTERN
def program(s: HasService1 with … HasServiceN) =
for {
a <- foo(s)("localhost", 42)
b <- bar(s)(a, 1024)
...
} yield z
THE READER MONAD
case class Reader[-R, +A](provide: R => A) { self =>
def map[B](f: A => B) = flatMap(a => Reader.point(f(a)))
def flatMap[R1 <: R, B](f: A => Reader[R1, B]) =
Reader[R, B](r => f(self.provide(r)).provide(r))
}
object Reader {
def point[A](a: => A): Reader[Any, A] = Reader(_ => a)
def environment[R]: Reader[R, R] = Reader(identity)
}
THE READER MONAD
def program: Reader[HasService1 with ..., Unit] =
for {
a <- foo("localhost", 42)
b <- bar(a, 1024)
...
} yield z
THE READER MONAD TRANSFORMER
ReaderT[IO, R, A]
THE READER MONAD TRANSFORMER
ReaderT[IO, R, A]
IO
ReaderT
ZIO ENVIRONMENT
ZIO ENVIRONMENT
ZIO ENVIRONMENT
ZIO[R, E, A]*
ZIO ENVIRONMENT
ZIO[R, E, A]*
type UIO [ +A] = ZIO[Any, Nothing, A]
type Task[ +A] = ZIO[Any, Throwable, A]
type IO [+E, +A] = ZIO[Any, E, A]
*
ZIO ENVIRONMENT
ZIO[R, E, A]
Environment Type
ZIO ENVIRONMENT
ZIO[R, E, A]
Failure Type
ZIO ENVIRONMENT
ZIO[R, E, A]
Success Type
ZIO ENVIRONMENT
ZIO[Console, IOException, Unit]
Requires Console
ZIO ENVIRONMENT
Might Fail with
IOException
ZIO[Console, IOException, Unit]
ZIO ENVIRONMENT
Might Succeed
with Unit
ZIO[Console, IOException, Unit]
ZIO ENVIRONMENT: EXAMPLE
val program: ZIO[Console with Persistence, IOException, Unit] =
for {
_ <- putStrLn("Good morning, what is your name?")
name <- getStrLn
_ <- savePreferences(name)
_ <- putStrLn(s"Good to meet you, $name!")
} yield ()
ZIO ENVIRONMENT: CORE
sealed trait ZIO[-R, +E, +A] {
...
def provide(environment: R): ZIO[Any, E, A] = ...
}
object ZIO {
def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ...
def access[R, E, A](f: R => A): ZIO[R, Nothing, A] =
accessM(ZIO.succeed(_))
def environment[R]: ZIO[R, Nothing, R] = access(identity)
}
ZIO ENVIRONMENT: CORE
sealed trait ZIO[-R, +E, +A] {
...
def provide(environment: R): ZIO[Any, E, A] = ...
}
object ZIO {
def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ...
def access[R, E, A](f: R => A): ZIO[R, Nothing, A] =
accessM(ZIO.succeed(_))
def environment[R]: ZIO[R, Nothing, R] = access(identity)
}
ZIO ENVIRONMENT: CORE
sealed trait ZIO[-R, +E, +A] {
...
def provide(environment: R): ZIO[Any, E, A] = ...
}
object ZIO {
def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ...
def access[R, E, A](f: R => A): ZIO[R, Nothing, A] =
accessM(ZIO.succeed(_))
def environment[R]: ZIO[R, Nothing, R] = access(identity)
}
ZIO ENVIRONMENT: CORE
sealed trait ZIO[-R, +E, +A] {
...
def provide(environment: R): ZIO[Any, E, A] = ...
}
object ZIO {
def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ...
def access[R, E, A](f: R => A): ZIO[R, Nothing, A] =
accessM(ZIO.succeed(_))
def environment[R]: ZIO[R, Nothing, R] = access(identity)
}
ZIO ENVIRONMENT: TUTORIAL
// Module (contains service)
trait Console {
def console: Console.Service
}
ZIO ENVIRONMENT: TUTORIAL
object Console {
// Service definition:
trait Service {
def println(line: String): ZIO[Any, Nothing, Unit]
val readLine: ZIO[Any, IOException, String]
}
}
ZIO ENVIRONMENT: TUTORIAL
object ConsoleLive extends Console {
val console = new Console.Service {
def println(line: String): UIO[Unit] =
ZIO.effectTotal(scala.io.StdIn.println(line))
val readLine: IO[IOException, String] =
ZIO.effect(scala.io.StdIn.readLine()).refineOrDie {
case e : IOException => e
}
}
}
ZIO ENVIRONMENT: TUTORIAL
// Optional (but handy!) helpers:
package object console {
def println(line: String): ZIO[Console, Nothing, Unit] =
ZIO.accessM(_.console println line)
val readLine: ZIO[Console, IOException, String] =
ZIO.accessM(_.console.readLine)
}
ZIO ENVIRONMENT: TUTORIAL
val program: ZIO[Console, IOException, Unit] =
for {
_ <- putStrLn("Good morning, what is your name?")
name <- getStrLn
_ <- putStrLn(s"Good to meet you, $name!")
} yield ()
DefaultRuntime.unsafeRun(program.provide(ConsoleLive))
ZIO ENVIRONMENT: TUTORIAL
object ConsoleTest extends Console {
...
}
DefaultRuntime.unsafeRun(program.provide(ConsoleTest))
ZIO ENVIRONMENT: TUTORIAL
val MyEnvironment: R = ...
val MyRuntime: Runtime[R] =
Runtime(MyEnvironment, PlatformLive)
Your Own R Platform (thread pool, etc.)
ZIO ENVIRONMENT: TEACHABLE
trait Console { def console: Console.Service }
object Console {
trait Service {
def println(line: String): ZIO[Any, Nothing, Unit]
val readLine: ZIO[Any, IOException, String]
}
}
object ConsoleLive extends Console.Service {
def println(line: String) = ZIO.effectTotal(scala.io.StdIn.println(line))
val readLine =
ZIO.effect(scala.io.StdIn.readLine()).refineOrDie(JustIOException)
}
Module
ZIO ENVIRONMENT: TEACHABLE
trait Console { def console: Console.Service }
object Console {
trait Service {
def println(line: String): ZIO[Any, Nothing, Unit]
val readLine: ZIO[Any, IOException, String]
}
}
object ConsoleLive extends Console.Service {
def println(line: String) = ZIO.effectTotal(scala.io.StdIn.println(line))
val readLine =
ZIO.effect(scala.io.StdIn.readLine()).refineOrDie(JustIOException)
}
Service
ZIO ENVIRONMENT: TEACHABLE
trait Console { def console: Console.Service }
object Console {
trait Service {
def println(line: String): ZIO[Any, Nothing, Unit]
val readLine: ZIO[Any, IOException, String]
}
}
object ConsoleLive extends Console.Service {
def println(line: String) = ZIO.effectTotal(scala.io.StdIn.println(line))
val readLine =
ZIO.effect(scala.io.StdIn.readLine()).refineOrDie(JustIOException)
}
Implementation
ZIO ENVIRONMENT: COMPOSABLE
trait Console { def console: Console.Service }
trait Logging { def logging: Logging.Service }
trait Persistence { def persistence: Persistence.Service }
...
val program: ZIO[Console with Logging with Persistence,
AppError, Unit] = ...
ZIO ENVIRONMENT: PERFORMANT
scala.concurrent.Future
ZIO ENVIRONMENT: FULLY INFERABLE
val program =
for {
_ <- putStrLn("Good morning, what is your name?")
name <- getStrLn
_ <- savePreferences(name)
_ <- log.debug("Saved $name to configuration")
_ <- putStrLn(s"Good to meet you, $name!")
} yield ()
ZIO ENVIRONMENT: FULLY INFERABLE
val program: ZIO[Console, Error, Unit] = ...
^^^^^^^
Found: Console
Expected: Console with Persistence with Logging with
Config with Auth
ZIO ENVIRONMENT: CONCISE
trait Console { def console: Console.Service }
trait Logging { def logging: Logging.Service }
trait Persistence { def persistence: Persistence.Service }
...
type ProgramEnv = Console with Logging with Persistence
val program: ZIO[ProgramEnv, AppError, Unit] = ...
ZIO ENVIRONMENT: CONCISE
trait Console { def console: Console.Service }
trait Logging { def logging: Logging.Service }
trait Persistence { def persistence: Persistence.Service }
...
type Program[A] = ZIO[Console with Logging with Persistence,
AppError, A]
val program: Program[Unit] = ...
ZIO ENVIRONMENT: MODULAR
def fn1: ZIO[R1, E, A] = {
def fn2: ZIO[R2, E, B] = ...
val localEnvironment: R2 = ...
val v1 = fn2.provide(localEnvironment)
...
}
val globalEnvironment: R1 = ...
val v2 = fn1.provide(globalEnvironment)
...
ZIO ENVIRONMENT: INCREMENTAL
// Deeply nested code:
val myCode: Task[Unit] = …
for {
...
result <- database.query(q)
...
} yield ()
ZIO ENVIRONMENT: INCREMENTAL
type TaskDB[A] = ZIO[Database, Throwable, A]
// Now fully testable!
def myCodeV2: TaskDB[Unit] = …
for {
...
result <- database.query(q)
...
} yield ()
THE REANIMATION OF TAGLESS-FINAL
THE REANIMATION OF TAGLESS-FINAL
trait HasState[S] {
def state: HasState.Service[S]
}
object HasState {
trait Service[S] {
def state: Ref[S]
}
}
THE REANIMATION OF TAGLESS-FINAL
implicit def MSZIO[S, R <: HasState[S], E]:
MonadState[ZIO[R, E, ?], S] =
new MonadState[ZIO[R, E, ?], S] {
...
}
...
def program[F[_]: MonadState[S, ?]]: F[Unit] = …
program[ZIO[HasState[S], E]]
THE REANIMATION OF TAGLESS-FINAL
implicit def MSZIO[S, R <: HasState[S], E]:
MonadState[ZIO[R, E, ?], S] =
new MonadState[ZIO[R, E, ?], S] {
...
}
...
def program[F[_]: MonadState[MySt, ?]]: F[Unit] = …
program[ZIO[HasState[MySt], Err]]
THE (RE)RISING OF CAKE
THE (RE)RISING OF CAKE
// Traditional MONOLITHIC cake
trait MyModule extends
CacheModule with LoggingModule with
DatabaseModule with AuthModule {
def refreshCache: Unit = ...
def genFeed : Feed = ...
def trace : Unit = ...
}
THE (RE)RISING OF CAKE
// Next-generation MODULAR cake
object MyFunctions {
val refreshCache: ZIO[Cache, Error, Unit] = ...
val genFeed: ZIO[Database with Auth, Error, Feed] = ...
val trace: ZIO[Debug, Nothing, Unit] = ...
}
WRAP UP
COMPARISON MATRIX
Transformers Free Monads Eff Tagless Final
Environmental
Effects
Composable ✔ ✔ ✔ ✔ ✔
Performance 𐄂 𐄂 𐄂 ✔ ✔
Reasoning Up to Discipline Up to Discipline Up to Discipline Up to Discipline Up to Discipline
Easily Teachable 𐄂 ? ✔ 𐄂 ✔
Concision 𐄂 𐄂 𐄂 𐄂 ✔
Full Type Inference 𐄂 𐄂 𐄂 𐄂 ✔
Modularity ✔ ✔ ✔ 𐄂 ✔
Pinpoint Testability 𐄂 𐄂 𐄂 𐄂 ✔
GETTING STARTED WITH ZIO
✨ github.com/scalaz/scalaz-zio
✨ gitter.im/scalaz/scalaz-zio
✨ scalaz.github.io/scalaz-zio
Thank You!
Any questions?
Stalk me online at @jdegoes
Follow Wiem online @wiemzin

More Related Content

PPTX
Firebase - A real-time server
PDF
Introduction to PowerShell
PPTX
Fast-paced Introduction to Android Internals
PPTX
Automating with Ansible
PPTX
Introduction to ansible
PDF
Functional Domain Modeling - The ZIO 2 Way
ODP
Gatling
PPTX
Docker Networking - Common Issues and Troubleshooting Techniques
Firebase - A real-time server
Introduction to PowerShell
Fast-paced Introduction to Android Internals
Automating with Ansible
Introduction to ansible
Functional Domain Modeling - The ZIO 2 Way
Gatling
Docker Networking - Common Issues and Troubleshooting Techniques

What's hot (20)

PDF
Booting Android: bootloaders, fastboot and boot images
PDF
FreeSWITCH on Docker
PPTX
Rasperry Pi and TI CC2650 IPv6 border router
PDF
Grande porte cobol level 1 - versão 2.3.5
PDF
오픈스택 멀티노드 설치 후기
PDF
iSCSI Target Support for Ceph
PPTX
Gatling overview
PPTX
Docker and kubernetes_introduction
PPTX
Integration Group - Robot Framework
PPTX
Qt for beginners part 1 overview and key concepts
 
PDF
Rest api 테스트 수행가이드
PPTX
Android audio system(pcm데이터출력준비-서비스서버)
PPTX
IBM Spectrum scale object deep dive training
PPTX
Full Stack Visualization: Build A React App With A Sankey Diagram
PDF
DevOps Meetup ansible
ODP
Android crash debugging
PDF
SFO15-TR9: PSCI, ACPI (and UEFI to boot)
PDF
OpenStack 인스턴스 간략 사용자_매뉴얼(liberty)_v1
PPTX
Java 8 Lambda and Streams
PDF
Relax and Recover on POWER (Updated 05-2017)
Booting Android: bootloaders, fastboot and boot images
FreeSWITCH on Docker
Rasperry Pi and TI CC2650 IPv6 border router
Grande porte cobol level 1 - versão 2.3.5
오픈스택 멀티노드 설치 후기
iSCSI Target Support for Ceph
Gatling overview
Docker and kubernetes_introduction
Integration Group - Robot Framework
Qt for beginners part 1 overview and key concepts
 
Rest api 테스트 수행가이드
Android audio system(pcm데이터출력준비-서비스서버)
IBM Spectrum scale object deep dive training
Full Stack Visualization: Build A React App With A Sankey Diagram
DevOps Meetup ansible
Android crash debugging
SFO15-TR9: PSCI, ACPI (and UEFI to boot)
OpenStack 인스턴스 간략 사용자_매뉴얼(liberty)_v1
Java 8 Lambda and Streams
Relax and Recover on POWER (Updated 05-2017)
Ad

Similar to The Death of Final Tagless (20)

PDF
Berlin meetup
PDF
Introduction To Scala
PDF
Scala Quick Introduction
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
Functional Programming with Groovy
PDF
Functional programming in Scala
ODP
Beginning Scala Svcc 2009
PPTX
ZIO: Powerful and Principled Functional Programming in Scala
PDF
A swift introduction to Swift
PDF
Pooya Khaloo Presentation on IWMC 2015
PDF
Generic Functional Programming with Type Classes
PDF
Go ahead, make my day
PPTX
Thinking Functionally with JavaScript
DOCX
Basic python laboratoty_ PSPP Manual .docx
PDF
Functional Programming for OO Programmers (part 2)
PDF
Learning Functional Programming Without Growing a Neckbeard
PPT
Functional Programming In Java
Berlin meetup
Introduction To Scala
Scala Quick Introduction
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Functional Programming with Groovy
Functional programming in Scala
Beginning Scala Svcc 2009
ZIO: Powerful and Principled Functional Programming in Scala
A swift introduction to Swift
Pooya Khaloo Presentation on IWMC 2015
Generic Functional Programming with Type Classes
Go ahead, make my day
Thinking Functionally with JavaScript
Basic python laboratoty_ PSPP Manual .docx
Functional Programming for OO Programmers (part 2)
Learning Functional Programming Without Growing a Neckbeard
Functional Programming In Java
Ad

More from John De Goes (20)

PDF
Refactoring Functional Type Classes
PDF
One Monad to Rule Them All
PDF
Error Management: Future vs ZIO
PDF
Atomically { Delete Your Actors }
PDF
Scalaz Stream: Rebirth
PDF
Scalaz Stream: Rebirth
PDF
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
PDF
ZIO Queue
PDF
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
PDF
Scalaz 8: A Whole New Game
PDF
Scalaz 8 vs Akka Actors
PDF
Orthogonal Functional Architecture
PDF
The Design of the Scalaz 8 Effect System
PDF
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
PDF
Post-Free: Life After Free Monads
PDF
Streams for (Co)Free!
PDF
MTL Versus Free
PDF
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
PDF
Halogen: Past, Present, and Future
PDF
All Aboard The Scala-to-PureScript Express!
Refactoring Functional Type Classes
One Monad to Rule Them All
Error Management: Future vs ZIO
Atomically { Delete Your Actors }
Scalaz Stream: Rebirth
Scalaz Stream: Rebirth
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Queue
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Scalaz 8: A Whole New Game
Scalaz 8 vs Akka Actors
Orthogonal Functional Architecture
The Design of the Scalaz 8 Effect System
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
Post-Free: Life After Free Monads
Streams for (Co)Free!
MTL Versus Free
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
Halogen: Past, Present, and Future
All Aboard The Scala-to-PureScript Express!

Recently uploaded (20)

PDF
Electronic commerce courselecture one. Pdf
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPTX
Cloud computing and distributed systems.
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Encapsulation theory and applications.pdf
PDF
KodekX | Application Modernization Development
PDF
Empathic Computing: Creating Shared Understanding
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Approach and Philosophy of On baking technology
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Electronic commerce courselecture one. Pdf
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
The Rise and Fall of 3GPP – Time for a Sabbatical?
Review of recent advances in non-invasive hemoglobin estimation
Building Integrated photovoltaic BIPV_UPV.pdf
Reach Out and Touch Someone: Haptics and Empathic Computing
Cloud computing and distributed systems.
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
“AI and Expert System Decision Support & Business Intelligence Systems”
NewMind AI Weekly Chronicles - August'25 Week I
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Diabetes mellitus diagnosis method based random forest with bat algorithm
Encapsulation theory and applications.pdf
KodekX | Application Modernization Development
Empathic Computing: Creating Shared Understanding
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Approach and Philosophy of On baking technology
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Build a system with the filesystem maintained by OSTree @ COSCUP 2025

The Death of Final Tagless

  • 1. The Death of Tagless Final John A. De Goes — @jdegoes Wiem Zine Elabidine — @wiemzin Amsterdam.scala & FP AMS Xebia, Amsterdam March 2, 2019
  • 4. First-Class Effects Tagless Final ZIO Environment Wrap Up
  • 6. PROGRAMS AS STATEMENTS def main: Unit = { println("Good morning, what is your name?") val name = readLine() println(s"Good to meet you, $name!") }
  • 7. PROGRAMS AS STATEMENTS val example: Double = … val value = example val values = List.fill(10)(value) val plusone = value => value + 1
  • 8. PROGRAMS AS STATEMENTS def main: Unit = … val program = main val programs = List.fill(10)(main) val retried = program => program.retried(2.times)
  • 10. BENEFITS OF VALUES def main: Unit = { println("Good morning, what is your name?") val name = readLine() println(s"Good to meet you, $name!") } Procedural Effects Functional Effects
  • 11. Functional effects are immutable data structures that merely describe sequences of operations. At the end of the world, the data structure has to be impurely “interpreted” to real world effects.
  • 13. case class PrintLine(text: String) Functional Effects “Describe”
  • 14. PROGRAMS AS VALUES IO[A] A description of an effect that when unsafely interpreted, will succeed with a value of type A
  • 15. PROGRAMS AS VALUES class IO[+A](val unsafeInterpret: () => A) { s => def map[B](f: A => B) = flatMap(f.andThen(IO.effect(_))) def flatMap[B](f: A => IO[B]): IO[B] = IO.effect(f(s.unsafeInterpret()).unsafeInterpret()) } object IO { def effect[A](eff: => A) = new IO(() => eff) }
  • 16. PROGRAMS AS VALUES def putStrLn(line: String): IO[Unit] = IO.effect(println(line)) val getStrLn: IO[String] = IO.effect(scala.io.StdIn.readLine())
  • 17. PROGRAMS AS VALUES val main = for { _ <- putStrLn("Good morning, " + "what is your name?") name <- getStrLn _ <- putStrLn(s"Good to meet you, $name!") } yield ()
  • 18. PROGRAMS AS VALUES val main = … val program = main val programs = List.fill(10)(main) val retried = program => program.retried(2.times)
  • 19. PROGRAMS AS VALUES def loadTest(url: String, n: Int) = val policy = Schedule.recurs(10).jittered val worker = client.get(url).retry(policy) val workers = List.fill(n)(worker) IO.collectAllPar(workers) }
  • 20. PROGRAMS AS VALUES def loadTest(url: String, n: Int) = val policy = Schedule.recurs(10).jittered val worker = client.get(url).retry(policy) val workers = List.fill(n)(worker) IO.collectAllPar(workers) }
  • 21. PROGRAMS AS VALUES def loadTest(url: String, n: Int) = val policy = Schedule.recurs(10).jittered val worker = client.get(url).retry(policy) val workers = List.fill(n)(worker) IO.collectAllPar(workers) }
  • 22. PROGRAMS AS VALUES def loadTest(url: String, n: Int) = val policy = Schedule.recurs(10).jittered val worker = client.get(url).retry(policy) val workers = List.fill(n)(worker) IO.collectAllPar(workers) }
  • 24. But!
  • 26. PROGRAMS AS (OPAQUE) VALUES def putStrLn(line: String): IO[Unit] = IO.effect(println(line)) val getStrLn: IO[String] = IO.effect(scala.io.StdIn.readLine())
  • 28. BACK TO BASICS: JAVA 101 interface Console { void println(String line); String readLine(); }
  • 29. BACK TO BASICS: JAVA 101 public void program(Console console) { console.println("Good morning, " + "what is your name?"); String name = console.readLine(); console.println(s"Good to meet you, $name!"); }
  • 30. BACK TO BASICS: JAVA 101 class LiveConsole implements Console { … } class TestConsole implements Console { … } // In production: program(new LiveConsole()); // In tests: program(new TestConsole());
  • 32. IO INTERFACES trait Console { def println(line: String): IO[Unit] val readLine: IO[String] }
  • 33. PURE PROGRAM WITH INTERFACE def program(c: Console) = for { _ <- c.println("Good morning, " + "what is your name?") name <- c.readLine _ <- c.println(s"Good to meet you, $name!") } yield ()
  • 34. POLYMORPHIC EFFECT trait Console[IO[_]] { def println(String line): IO[Unit] val readLine: IO[String] }
  • 35. MAKING IT EVEN MORE OBSCURE trait Console[F[_]] { def println(String line): F[Unit] val readLine: F[String] }
  • 36. THE GLORIOUS TAGLESS-FINAL TYPE CLASS trait Console[F[_]] { def println(String line): F[Unit] val readLine: F[String] } object Console { def apply[F[_]](implicit F: Console[F]) = F }
  • 37. EFFECT-POLYMORPHIC PROGRAMS def program[F[_]: Console: Monad] = for { _ <- Console[F].println( "Good morning, " + "what is your name?") name <- Console[F].readLine _ <- Console[F].println( s"Good to meet you, $name!") } yield ()
  • 38. TEST VS PRODUCTION EFFECTS case class TestIO[A](...) implicit val TestConsole = new Console[TestIO] { … } implicit val LiveConsole = new Console[IO] { … } // In production: program[IO] : IO[Unit] // In tests: program[TestIO] : TestIO[Unit]
  • 42. TAGLESS-FINAL IS PRETTY EASY! Functional Effects Parametric Polymorphism F
  • 43. TAGLESS-FINAL IS EASY-ISH! Functional Effects Parametric Polymorphism F[_] Higher-Kinded Types
  • 44. TAGLESS-FINAL ISN’T THAT HARD! Functional Effects Parametric Polymorphism trait Console[F[_]] { … } Higher-Kinded Types Type Classes
  • 45. TAGLESS-FINAL COULD BE MUCH HARDER! Functional Effects Parametric Polymorphism implicit val TestConsole = new Console[TestIO] { … } Higher-Kinded Types Type Classes Type Class Instances
  • 46. TAGLESS-FINAL IS SORT OF HARD! Functional Effects Parametric Polymorphism new Console[({type F[A] = State[TestData, A]})#F] { … } Higher-Kinded Types Type Classes Type Class Instances Partial Type Application
  • 47. OMG, TAGLESS-FINAL IS THE WORST!!! Functional Effects Parametric Polymorphism def program[F[_]: Monad: Console: Database] = ... Higher-Kinded Types Type Classes Type Class Instances Partial Type Application Monad Hierarchy
  • 48. TAGLESS-FINAL IS MANY THINGS EASY IS NOT ONE OF THEM
  • 49. ☠ TYPE CLASS ABUSE ☠ THE DARK SIDE OF TAGLESS-FINAL REAL TYPE CLASS Defines common structure across types through lawful operations, enabling abstraction. FAKE TYPE CLASS Defines a common interface across types through ad hoc polymorphism, enabling testing.
  • 50. ☠ TYPE CLASS ABUSE ☠ THE DARK SIDE OF TAGLESS-FINAL REAL TYPE CLASS Defines common structure across types through lawful operations, enabling abstraction. FAKE TYPE CLASS Defines a common interface across types through ad hoc polymorphism, enabling testing.
  • 51. ☠ BIG BANG INTRODUCTION ☠ THE DARK SIDE OF TAGLESS-FINAL def program[F[_]: Console: Monad] = for { _ <- Console[F].println( "Good morning, " + "what is your name?") name <- Console[F].readLine _ <- Console[F].println( s"Good to meet you, $name!") } yield ()
  • 52. ☠ TEDIOUS REPETITION ☠ THE DARK SIDE OF TAGLESS-FINAL def genFeed[F[_]: Monad: Logging: UserDatabase: ProfileDatabase: RedisCache: GeoIPService: AuthService: SessionManager: Localization: Config: EventQueue: Concurrent: Async: MetricsManager]: F[Feed] = ???
  • 53. ☠ STUBBORN REPETITION ☠ THE DARK SIDE OF TAGLESS-FINAL def genFeed[F[_]: Everything]: F[Feed] = ???
  • 54. ☠ COMPLETELY NON-INFERABLE ☠ THE DARK SIDE OF TAGLESS-FINAL def genFeed = ???
  • 55. ☠ FAKE PARAMETRIC GUARANTEES ☠ def innocent[F[_]: Monad]: F[Unit] = { def effect[A](a: => A): F[A] = Monad[F].point(()).map(_ => a) println("What guarantees?") effect(System.exit(42)) } THE DARK SIDE OF TAGLESS-FINAL
  • 56. THE DEATH OF TAGLESS-FINAL
  • 57. THE DEATH OF TAGLESS-FINAL
  • 59. ZIO is a zero-dependency Scala library for asynchronous and concurrent programming.
  • 61. BACK TO BASICS trait Console { def println(line: String): IO[Unit] val readLine: IO[String] }
  • 62. BACK TO BASICS def program(c: Console) = for { _ <- c.println("Good morning, " + "What is your name?") name <- c.readLine _ <- c.println(s"Good to meet you, $name!") } yield ()
  • 63. BACK TO BASICS def program(c: Console, p: Persistence) = for { _ <- c.println("Good morning, " + "what is your name?") name <- c.readLine _ <- p.savePreferences(name) _ <- c.println(s"Good to meet you, $name!") } yield ()
  • 64. BACK TO BASICS def program(s1: Service1, s2: Service2, s3: Service3, … sn: ServiceN) = for { a <- foo(s1, s9, s3)("localhost", 42) b <- bar(sn, s19, s3)(a, 1024) ... } yield z
  • 65. BACK TO BASICS def program(s1: Service1, s2: Service2, s3: Service3, … sn: ServiceN) = for { a <- foo(s1, s9, s3)("localhost", 42) b <- bar(sn, s19, s3)(a, 1024) ... } yield z
  • 66. THE MODULE PATTERN trait HasConsole { def console: HasConsole.Service } object HasConsole { trait Service { def println(line: String): IO[Unit] val readLine: IO[String] } }
  • 67. THE MODULE PATTERN trait HasConsole { def console: HasConsole.Service } object HasConsole { trait Service { def println(line: String): IO[Unit] val readLine: IO[String] } }
  • 68. THE MODULE PATTERN def program(s: HasConsole with HasPersistence) = for { _ <- s.console.println("What is your name?") name <- s.console.readLine _ <- s.persistence.savePreferences(name) _ <- s.console.println(s"Good to meet” + ” you, $name!") } yield ()
  • 69. THE MODULE PATTERN def program(s: HasService1 with … HasServiceN) = for { a <- foo(s)("localhost", 42) b <- bar(s)(a, 1024) ... } yield z
  • 70. THE READER MONAD case class Reader[-R, +A](provide: R => A) { self => def map[B](f: A => B) = flatMap(a => Reader.point(f(a))) def flatMap[R1 <: R, B](f: A => Reader[R1, B]) = Reader[R, B](r => f(self.provide(r)).provide(r)) } object Reader { def point[A](a: => A): Reader[Any, A] = Reader(_ => a) def environment[R]: Reader[R, R] = Reader(identity) }
  • 71. THE READER MONAD def program: Reader[HasService1 with ..., Unit] = for { a <- foo("localhost", 42) b <- bar(a, 1024) ... } yield z
  • 72. THE READER MONAD TRANSFORMER ReaderT[IO, R, A]
  • 73. THE READER MONAD TRANSFORMER ReaderT[IO, R, A] IO ReaderT
  • 77. ZIO ENVIRONMENT ZIO[R, E, A]* type UIO [ +A] = ZIO[Any, Nothing, A] type Task[ +A] = ZIO[Any, Throwable, A] type IO [+E, +A] = ZIO[Any, E, A] *
  • 78. ZIO ENVIRONMENT ZIO[R, E, A] Environment Type
  • 79. ZIO ENVIRONMENT ZIO[R, E, A] Failure Type
  • 80. ZIO ENVIRONMENT ZIO[R, E, A] Success Type
  • 82. ZIO ENVIRONMENT Might Fail with IOException ZIO[Console, IOException, Unit]
  • 83. ZIO ENVIRONMENT Might Succeed with Unit ZIO[Console, IOException, Unit]
  • 84. ZIO ENVIRONMENT: EXAMPLE val program: ZIO[Console with Persistence, IOException, Unit] = for { _ <- putStrLn("Good morning, what is your name?") name <- getStrLn _ <- savePreferences(name) _ <- putStrLn(s"Good to meet you, $name!") } yield ()
  • 85. ZIO ENVIRONMENT: CORE sealed trait ZIO[-R, +E, +A] { ... def provide(environment: R): ZIO[Any, E, A] = ... } object ZIO { def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ... def access[R, E, A](f: R => A): ZIO[R, Nothing, A] = accessM(ZIO.succeed(_)) def environment[R]: ZIO[R, Nothing, R] = access(identity) }
  • 86. ZIO ENVIRONMENT: CORE sealed trait ZIO[-R, +E, +A] { ... def provide(environment: R): ZIO[Any, E, A] = ... } object ZIO { def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ... def access[R, E, A](f: R => A): ZIO[R, Nothing, A] = accessM(ZIO.succeed(_)) def environment[R]: ZIO[R, Nothing, R] = access(identity) }
  • 87. ZIO ENVIRONMENT: CORE sealed trait ZIO[-R, +E, +A] { ... def provide(environment: R): ZIO[Any, E, A] = ... } object ZIO { def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ... def access[R, E, A](f: R => A): ZIO[R, Nothing, A] = accessM(ZIO.succeed(_)) def environment[R]: ZIO[R, Nothing, R] = access(identity) }
  • 88. ZIO ENVIRONMENT: CORE sealed trait ZIO[-R, +E, +A] { ... def provide(environment: R): ZIO[Any, E, A] = ... } object ZIO { def accessM[R, E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ... def access[R, E, A](f: R => A): ZIO[R, Nothing, A] = accessM(ZIO.succeed(_)) def environment[R]: ZIO[R, Nothing, R] = access(identity) }
  • 89. ZIO ENVIRONMENT: TUTORIAL // Module (contains service) trait Console { def console: Console.Service }
  • 90. ZIO ENVIRONMENT: TUTORIAL object Console { // Service definition: trait Service { def println(line: String): ZIO[Any, Nothing, Unit] val readLine: ZIO[Any, IOException, String] } }
  • 91. ZIO ENVIRONMENT: TUTORIAL object ConsoleLive extends Console { val console = new Console.Service { def println(line: String): UIO[Unit] = ZIO.effectTotal(scala.io.StdIn.println(line)) val readLine: IO[IOException, String] = ZIO.effect(scala.io.StdIn.readLine()).refineOrDie { case e : IOException => e } } }
  • 92. ZIO ENVIRONMENT: TUTORIAL // Optional (but handy!) helpers: package object console { def println(line: String): ZIO[Console, Nothing, Unit] = ZIO.accessM(_.console println line) val readLine: ZIO[Console, IOException, String] = ZIO.accessM(_.console.readLine) }
  • 93. ZIO ENVIRONMENT: TUTORIAL val program: ZIO[Console, IOException, Unit] = for { _ <- putStrLn("Good morning, what is your name?") name <- getStrLn _ <- putStrLn(s"Good to meet you, $name!") } yield () DefaultRuntime.unsafeRun(program.provide(ConsoleLive))
  • 94. ZIO ENVIRONMENT: TUTORIAL object ConsoleTest extends Console { ... } DefaultRuntime.unsafeRun(program.provide(ConsoleTest))
  • 95. ZIO ENVIRONMENT: TUTORIAL val MyEnvironment: R = ... val MyRuntime: Runtime[R] = Runtime(MyEnvironment, PlatformLive) Your Own R Platform (thread pool, etc.)
  • 96. ZIO ENVIRONMENT: TEACHABLE trait Console { def console: Console.Service } object Console { trait Service { def println(line: String): ZIO[Any, Nothing, Unit] val readLine: ZIO[Any, IOException, String] } } object ConsoleLive extends Console.Service { def println(line: String) = ZIO.effectTotal(scala.io.StdIn.println(line)) val readLine = ZIO.effect(scala.io.StdIn.readLine()).refineOrDie(JustIOException) } Module
  • 97. ZIO ENVIRONMENT: TEACHABLE trait Console { def console: Console.Service } object Console { trait Service { def println(line: String): ZIO[Any, Nothing, Unit] val readLine: ZIO[Any, IOException, String] } } object ConsoleLive extends Console.Service { def println(line: String) = ZIO.effectTotal(scala.io.StdIn.println(line)) val readLine = ZIO.effect(scala.io.StdIn.readLine()).refineOrDie(JustIOException) } Service
  • 98. ZIO ENVIRONMENT: TEACHABLE trait Console { def console: Console.Service } object Console { trait Service { def println(line: String): ZIO[Any, Nothing, Unit] val readLine: ZIO[Any, IOException, String] } } object ConsoleLive extends Console.Service { def println(line: String) = ZIO.effectTotal(scala.io.StdIn.println(line)) val readLine = ZIO.effect(scala.io.StdIn.readLine()).refineOrDie(JustIOException) } Implementation
  • 99. ZIO ENVIRONMENT: COMPOSABLE trait Console { def console: Console.Service } trait Logging { def logging: Logging.Service } trait Persistence { def persistence: Persistence.Service } ... val program: ZIO[Console with Logging with Persistence, AppError, Unit] = ...
  • 101. ZIO ENVIRONMENT: FULLY INFERABLE val program = for { _ <- putStrLn("Good morning, what is your name?") name <- getStrLn _ <- savePreferences(name) _ <- log.debug("Saved $name to configuration") _ <- putStrLn(s"Good to meet you, $name!") } yield ()
  • 102. ZIO ENVIRONMENT: FULLY INFERABLE val program: ZIO[Console, Error, Unit] = ... ^^^^^^^ Found: Console Expected: Console with Persistence with Logging with Config with Auth
  • 103. ZIO ENVIRONMENT: CONCISE trait Console { def console: Console.Service } trait Logging { def logging: Logging.Service } trait Persistence { def persistence: Persistence.Service } ... type ProgramEnv = Console with Logging with Persistence val program: ZIO[ProgramEnv, AppError, Unit] = ...
  • 104. ZIO ENVIRONMENT: CONCISE trait Console { def console: Console.Service } trait Logging { def logging: Logging.Service } trait Persistence { def persistence: Persistence.Service } ... type Program[A] = ZIO[Console with Logging with Persistence, AppError, A] val program: Program[Unit] = ...
  • 105. ZIO ENVIRONMENT: MODULAR def fn1: ZIO[R1, E, A] = { def fn2: ZIO[R2, E, B] = ... val localEnvironment: R2 = ... val v1 = fn2.provide(localEnvironment) ... } val globalEnvironment: R1 = ... val v2 = fn1.provide(globalEnvironment) ...
  • 106. ZIO ENVIRONMENT: INCREMENTAL // Deeply nested code: val myCode: Task[Unit] = … for { ... result <- database.query(q) ... } yield ()
  • 107. ZIO ENVIRONMENT: INCREMENTAL type TaskDB[A] = ZIO[Database, Throwable, A] // Now fully testable! def myCodeV2: TaskDB[Unit] = … for { ... result <- database.query(q) ... } yield ()
  • 108. THE REANIMATION OF TAGLESS-FINAL
  • 109. THE REANIMATION OF TAGLESS-FINAL trait HasState[S] { def state: HasState.Service[S] } object HasState { trait Service[S] { def state: Ref[S] } }
  • 110. THE REANIMATION OF TAGLESS-FINAL implicit def MSZIO[S, R <: HasState[S], E]: MonadState[ZIO[R, E, ?], S] = new MonadState[ZIO[R, E, ?], S] { ... } ... def program[F[_]: MonadState[S, ?]]: F[Unit] = … program[ZIO[HasState[S], E]]
  • 111. THE REANIMATION OF TAGLESS-FINAL implicit def MSZIO[S, R <: HasState[S], E]: MonadState[ZIO[R, E, ?], S] = new MonadState[ZIO[R, E, ?], S] { ... } ... def program[F[_]: MonadState[MySt, ?]]: F[Unit] = … program[ZIO[HasState[MySt], Err]]
  • 113. THE (RE)RISING OF CAKE // Traditional MONOLITHIC cake trait MyModule extends CacheModule with LoggingModule with DatabaseModule with AuthModule { def refreshCache: Unit = ... def genFeed : Feed = ... def trace : Unit = ... }
  • 114. THE (RE)RISING OF CAKE // Next-generation MODULAR cake object MyFunctions { val refreshCache: ZIO[Cache, Error, Unit] = ... val genFeed: ZIO[Database with Auth, Error, Feed] = ... val trace: ZIO[Debug, Nothing, Unit] = ... }
  • 116. COMPARISON MATRIX Transformers Free Monads Eff Tagless Final Environmental Effects Composable ✔ ✔ ✔ ✔ ✔ Performance 𐄂 𐄂 𐄂 ✔ ✔ Reasoning Up to Discipline Up to Discipline Up to Discipline Up to Discipline Up to Discipline Easily Teachable 𐄂 ? ✔ 𐄂 ✔ Concision 𐄂 𐄂 𐄂 𐄂 ✔ Full Type Inference 𐄂 𐄂 𐄂 𐄂 ✔ Modularity ✔ ✔ ✔ 𐄂 ✔ Pinpoint Testability 𐄂 𐄂 𐄂 𐄂 ✔
  • 117. GETTING STARTED WITH ZIO ✨ github.com/scalaz/scalaz-zio ✨ gitter.im/scalaz/scalaz-zio ✨ scalaz.github.io/scalaz-zio
  • 118. Thank You! Any questions? Stalk me online at @jdegoes Follow Wiem online @wiemzin