Belajar Scala - Functional Effects: cats-effect & ZIO
Episode 11 of 23

Belajar Scala - Functional Effects: cats-effect & ZIO

Menjembatani dunia pure dan dunia nyata dengan effect system: IO dari cats-effect dan ZIO sebagai deskripsi program yang dapat dikomposisi, resource safety lewat Resource dan Scope, serta cancellation yang bersih — plus kapan memilih library versus full-stack effect.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sejauh ini, kalian belajar bahwa Scala menyukai pure function: input sama → output sama. Tapi program sungguhan harus membaca file, memanggil API, dan menulis ke database — semua operasi yang punya efek samping. Bagaimana kalian mempertahankan kemurnian di dunia yang tidak murni? Jawabannya: effect system.

Mengapa ini penting? Karena effect system adalah inti dari "functional programming di produksi". Library seperti cats-effect dan ZIO membuat efek samping menjadi nilai yang bisa dikomposisi — bisa dibangun, diprogram, dibatalkan, dan diuji dengan aman. Setelah episode ini, kalian akan melihat dunia backend Scala dengan mata yang berbeda.

Masalah dengan Efek Samping Langsung

Bandingkan dua cara menulis efek samping:

Efek langsung - dijalankan saat dibangun
def readFile(path: String): String =
  val source = scala.io.Source.fromFile(path)
  try source.mkString finally source.close()

Masalahnya: fungsi ini tidak pure. Ia membaca disk, bergantung pada sistem, bisa melempar exception — dan kalian tidak bisa menundanya, membatalkannya, atau mengujinya tanpa benar-benar membaca file.

Konsep Dasar: Program sebagai Nilai

Effect system membalik logika: kalian tidak "menjalankan" efek saat menulis kode, kalian mendeskripsikan efek sebagai nilai, lalu menjalankannya nanti di satu titik.

cats-effect IO

IO[A] adalah deskripsi dari komputasi yang ketika dijalankan akan menghasilkan A:

IO sebagai deskripsi program
import cats.effect.IO
 
val program: IO[String] =
  IO.println("mulai...") *>
    IO.readLine.map(name => s"Hello, $name!")
 
// belum menjalankan apa pun sampai ini:
program.unsafeRunSync()

Perhatikan: IO.println dan IO.readLine tidak mencetak/membaca saat dipanggil — mereka membangun deskripsi. Eksekusi terjadi hanya saat unsafeRunSync(). Ini membuka kemungkinan:

  • Menunda eksekusi kapan pun diinginkan.
  • Menggabungkan program dengan operator kombinator.
  • Membatalkan program yang berjalan.
  • Menguji program tanpa efek samping nyata (ganti dengan fake).

Komposisi Program

Operator penting di IO:

Komposisi IO
import cats.effect.IO
import cats.syntax.all.*
 
val readConfig: IO[Map[String, String]] = IO.pure(Map("port" -> "8080"))
 
val readDb: IO[Int] = IO.pure(42)
 
val combined: IO[String] =
  for
    cfg <- readConfig
    count <- readDb
  yield s"config ${cfg("port")}, total $count"

for-comprehension (sintaks gula untuk flatMap) membuat komposisi program multi-langkah terbaca seperti kode imperatif, tetapi tetap pure.

Resource Safety: Resource dan Scope

Efek samping membutuhkan pembersihan — file harus ditutup, koneksi harus dilepas. Cats-effect menyediakan Resource:

Resource menjamin cleanup
import cats.effect.{IO, Resource}
 
def openFile(path: String): Resource[IO, String] =
  Resource.make(
    IO { scala.io.Source.fromFile(path).mkString }
  )(content => IO.println(s"cleanup file $path"))
 
val program: IO[Unit] =
  openFile("data.txt").use { content =>
    IO.println(s"file berisi ${content.length} karakter")
  }

Kontrak Resource.make(acquire)(release) menjamin release dipanggil — baik sukses, gagal, maupun dibatalkan. Ini menghilangkan kelas bug "lupa tutup koneksi". ZIO memiliki mekanisme setara bernama Scope.

Cancellation: Efek yang Bisa Dihentikan

Kekuatan lain effect system: cancellation yang bersih. Program yang berjalan bisa dibatalkan — misalnya karena timeout — dan effect system memastikan resource tetap dibersihkan:

Timeout membatalkan program
import cats.effect.{IO, IOApp}
import scala.concurrent.duration.*
 
object Main extends IOApp.Simple:
  val run: IO[Unit] =
    for
      fiber <- IO.sleep(10.seconds).start   // mulai di fiber terpisah
      _ <- IO.sleep(1.second)               // tunggu 1 detik
      _ <- fiber.cancel                     // batalkan sebelum selesai
      _ <- IO.println("dibatalkan")
    yield ()

Konsep fiber di sini akan kita bedah penuh di episode 12. Yang penting sekarang: pembatalan tidak meninggalkan resource menggantung.

ZIO: Effect System Lengkap

ZIO adalah effect system lain yang lebih "full-stack": ia membawa environment, error, dan effect dalam satu tipe:

ZIO - efek dengan environment dan error
import zio.*
 
object Main extends ZIOAppDefault:
  def run =
    for
      _ <- Console.printLine("Siapa nama kalian?")
      name <- Console.readLine
      _ <- Console.printLine(s"Hello, $name!")
    yield ()

ZIO[R, E, A] punya tiga parameter: R (environment/dependency), E (error), dan A (hasil). Error di-level tipe — bukan exception yang lolos. ZIOAppDefault menyediakan entry point dengan runtime terkelola.

Typeclass vs Full Stack

Aspekcats-effectZIO
PosisiLibrary — typeclass (Sync, Async)Framework lengkap dengan runtime sendiri
TipeIO[A]ZIO[R, E, A]
KonsepEfek polos, kompatibel luasEnvironment, error, dan effect dalam satu tipe
Ekosistemhttp4s, fs2, doobie (typelevel)zio-http, zio-streams, quill
FokusInterop dengan library FP lainSatu stack terintegrasi

Tip

Pilih berdasarkan kebutuhan: jika kalian ingin kebebasan memilih library per lapisan, cats-effect (ekosistem Typelevel) lebih fleksibel. Jika ingin satu stack konsisten dengan environment injection bawaan, ZIO mengurangi keputusan arsitektur. Keduanya adalah pilihan produksi yang matang — pilih sesuai selera tim, bukan "yang paling baru".

Kapan Memakai Effect System

Jawaban praktis: semua program yang melakukan I/O — HTTP server, CLI yang membaca input, aplikasi database. Mulai dari proyek kecil pun, membungkus efek dalam IO/ZIO sejak awal lebih murah daripada merombak nanti. Untuk komputasi murni (memproses List), effect system tidak perlu — di situlah map/filter biasa bekerja.

100%

Common Pitfalls

  • Memanggil unsafeRunSync di mana-mana — efek harus dikomposisi dan dijalankan sekali di entry point; jangan membongkar IO di tengah alur.
  • Effect yang langsung mengeksekusi saat dibangun — jika kode eksekusi (bukan deskripsi) terjadi di dalam body IO, ia bocor; semua I/O harus berada di dalam konstruktor effect.
  • flatMap berantai tanpa for — untuk 3+ langkah, for-comprehension jauh lebih terbaca.
  • Melupakan Resource/Scope — resource tanpa akuisisi-terkelola adalah potensi leak di produksi.

Penutup

Inti yang harus dibawa pulang:

  • Effect system mengubah efek samping menjadi nilai yang dapat dikomposisi — deskripsi, bukan eksekusi.
  • cats-effect IO[A] dan ZIO[R, E, A] adalah dua implementasi utama.
  • Resource (cats-effect) dan Scope (ZIO) menjamin cleanup otomatis.
  • Cancellation memungkinkan program dibatalkan dengan bersih — fondasi timeout dan shutdown.
  • cats-effect = library fleksibel; ZIO = full-stack terintegrasi; pilih sesuai konteks.

Di episode 12 selanjutnya, kita masuk ke ranah yang menantang: concurrency dan parallelismFuture, fiber, Ref, Queue, dan structured concurrency. Sampai jumpa di episode 12!

Belajar Scala - Functional Effects: cats-effect & ZIO | Belajar Scala