Effect Systems
Effect systems describe side effects as lazy, composable values, giving referential transparency, structured concurrency, resource safety, and fiber-based (green thread) concurrency.
Effect systems describe side effects as lazy, composable values, giving referential transparency, structured concurrency, resource safety, and fiber-based (green thread) concurrency.
34.1 Cats Effect 3
IO.delay/IO.blocking/IO.interruptible — suspend side effects.
Resource — safe acquisition/release (Resource.make(acquire)(release), .use).
Ref (atomic state), Deferred (one-shot promise), Queue, Semaphore, Supervisor.
Cancellation, timeouts (.timeout), retries, handleErrorWith, attempt.
Typeclass hierarchy: MonadCancel, Spawn, Concurrent, Temporal, Sync, Async.
//> using dep org.typelevel::cats-effect:3.5.7
import cats.effect.*
import cats.syntax.all.*
import scala.concurrent.duration.*
object Main extends IOApp.Simple:
val hello: IO[Unit] = IO.println("Hello")
def countdown(n: Int): IO[Unit] =
if n == 0 then IO.println("Liftoff!")
else IO.println(n) >> IO.sleep(1.second) >> countdown(n - 1)
def run: IO[Unit] =
for
_ <- hello
fiber <- countdown(3).start // run concurrently on a fiber
_ <- IO.println("doing other work")
_ <- fiber.join
results <- List(1, 2, 3).parTraverse(n => IO.sleep(100.millis).as(n * 2))
_ <- IO.println(results)
winner <- IO.race(IO.sleep(1.second).as("slow"), IO.sleep(10.millis).as("fast"))
_ <- IO.println(winner)
yield ()val fileResource: Resource[IO, scala.io.Source] =
Resource.fromAutoCloseable(IO.blocking(scala.io.Source.fromFile("data.txt")))
val lines: IO[List[String]] = fileResource.use(src => IO.blocking(src.getLines().toList))
val counter: IO[Int] = for
ref <- Ref.of[IO, Int](0)
_ <- (1 to 100).toList.parTraverse_(_ => ref.update(_ + 1))
n <- ref.get
yield n34.2 ZIO 2
ZIO[R, E, A]: needs environment R, may fail with E, succeeds with A. Features: ZLayer dependency injection, typed errors, fibers, Ref, Queue, Hub, STM (software transactional memory), Schedule (retries), ZIO Streams, ZIO Test, ZIO HTTP, ZIO Config, ZIO JSON.
//> using dep dev.zio::zio:2.1.14
import zio.*
object Main extends ZIOAppDefault:
trait UserRepo:
def find(id: Int): IO[String, String]
object UserRepo:
val live: ULayer[UserRepo] = ZLayer.succeed(new UserRepo:
def find(id: Int) = if id == 1 then ZIO.succeed("Ann") else ZIO.fail(s"no user $id"))
val program: ZIO[UserRepo, String, Unit] =
for
repo <- ZIO.service[UserRepo]
name <- repo.find(1)
_ <- Console.printLine(s"Found $name").orDie
_ <- ZIO.foreachPar(1 to 3)(i => ZIO.sleep(100.millis) *> ZIO.succeed(i))
yield ()
def run = program.provide(UserRepo.live).catchAll(e => Console.printLine(s"Error: $e"))34.3 Kyo & Ox
Kyo — algebraic-effects-style library with composable effect types.
Ox (SoftwareMill) — direct-style structured concurrency on JDK 21 virtual threads (par, race, supervised, channels).
34.4 Choosing
standard library; fine for simple async tagless final / IO Typelevel ecosystem (http4s, fs2, doobie, skunk) ZIO[R, E, A] all-in-one batteries included ecosystem virtual threads, minimal abstraction