Functional Effects: Cats Effect and ZIO
Prerequisites: Futures and concurrency, Higher-kinded types
An effect system represents side effects — I/O, time, randomness, concurrency — as values that describe what to do. Nothing happens until the program runs the description at "the end of the world".
The core idea
// A tiny IO, to show the idea
final case class IO[A](run: () => A):
def map[B](f: A => B): IO[B] = IO(() => f(run()))
def flatMap[B](f: A => IO[B]): IO[B] = IO(() => f(run()).run())
object IO:
def delay[A](a: => A): IO[A] = IO(() => a)
def say(msg: String): IO[Unit] = IO.delay(println(msg))
@main def effects(): Unit =
val program = for
_ <- say("Hello")
_ <- say("World")
yield ()
println("Nothing has printed yet")
program.run()
program.run()
Nothing has printed yet
Hello
World
Hello
World
program is a value: building it printed nothing, and it can be run
twice, retried, or combined with others. Real effect systems build on this
idea with cancellation, resource safety, and lightweight threads
(fibers).
Cats Effect
// illustrative
//> using dep org.typelevel::cats-effect:3.6.1
import cats.effect.{IO, IOApp}
import cats.syntax.all.*
import scala.concurrent.duration.*
object Main extends IOApp.Simple:
def fetch(id: Int): IO[String] =
IO.sleep(200.millis) *> IO.pure(s"result $id")
val run: IO[Unit] =
for
results <- (1 to 5).toList.parTraverse(fetch) // concurrently
_ <- IO.println(results.mkString(", "))
winner <- IO.race(fetch(1), IO.sleep(1.second))
_ <- IO.println(winner)
yield ()
result 1, result 2, result 3, result 4, result 5
Left(result 1)
ZIO
// illustrative
//> using dep dev.zio::zio:2.1.17
import zio.*
object Main extends ZIOAppDefault:
def fetch(id: Int): Task[String] = ZIO.succeed(s"result $id").delay(200.millis)
val run =
for
results <- ZIO.foreachPar(1 to 5)(fetch).withParallelism(3)
_ <- Console.printLine(results.mkString(", "))
value <- fetch(9).timeout(1.second).retry(Schedule.recurs(3))
_ <- Console.printLine(value)
yield ()
Both libraries offer:
- Fibers — millions of lightweight threads, with safe cancellation.
- Resource safety —
Resource/ZIO.acquireReleaseguarantee cleanup even on errors or cancellation. - Concurrency tools — parallel traversal, races, timeouts, retries, queues, and concurrent state.
- Ecosystems — http4s, fs2, Doobie, and Skunk for Cats Effect; ZIO HTTP, ZIO Streams, and ZIO JDBC for ZIO.
Cats Effect leans on type classes and pairs with the Cats library; ZIO has
built-in typed errors and dependency injection (ZLayer). Teams usually
pick one and use its ecosystem consistently.