Program Scala yang siap untuk produksi hampir selalu berinteraksi dengan lingkungan di luar sistemnya, yaitu mencetak ke terminal, membaca berkas, memanggil jaringan, atau menunggu waktu. Tindakan-tindakan itu adalah efek samping (side effect), dan effect system adalah pendekatan yang mengelolanya secara eksplisit pada tingkat tipe. Topik ini memberi kesadaran atas dua effect system utama di ekosistem Scala, yaitu cats-effect dengan tipenya IO dan ZIO dengan tipenya ZIO, melalui contoh kecil yang benar-benar berjalan. Setelah topik ini dibaca, pola IO[A] dan ZIO[R, E, A] pada kode produksi dapat dikenali sebagai deskripsi program yang dieksekusi pada titik tertentu, dan sumber resmi untuk belajar lebih lanjut tersedia.
Masalah yang dijawab effect system#
Sebuah fungsi bertipe def kirim(s: String): Int hanya memperlihatkan hasil akhirnya, sedangkan efek samping yang dilakukannya di dalam tidak terlihat pada tipe. Pencetakan, akses berkas, dan panggilan jaringan dapat tersembunyi di mana saja, sehingga urutan eksekusi dan kegagalannya sulit disusun dengan aman. Future pada pustaka standar sudah menunjukkan sebagian jalan keluarnya, tetapi Future langsung dijadwalkan pada saat dibuat, sehingga pendefinisian dan eksekusinya menyatu dan tidak dapat dipisahkan. Effect system memisahkan keduanya dengan merepresentasikan efek sebagai nilai deskripsi, yaitu objek yang memuat rencana kerja tanpa menjalankannya.
Deskripsi itu kemudian dikomposisikan dengan map dan flatMap, sama seperti transformasi pada Option dan Either, hingga menjadi satu program utuh. Eksekusi baru terjadi di tepi program, misalnya pada titik masuk aplikasi, sehingga kode di antaranya memanipulasi nilai tanpa menjalankan apa pun. Persoalan kedua yang dijawab adalah keamanan sumber daya (resource safety), yaitu jaminan bahwa berkas yang dibuka tetap ditutup meskipun pemrosesannya gagal. Disiplin try dan finally yang ditulis manual mudah terlewat pada salah satu jalur kegagalan, sedangkan effect system menyediakan konstruksi khusus yang menutup sumber daya secara otomatis.
IO pada cats-effect sebagai deskripsi program#
cats-effect merepresentasikan deskripsi program dengan tipe IO[A], yaitu nilai yang memuat rencana kerja dan hasilnya bertipe A. Nilai run bertipe IO[Unit] pada contoh berikut menyusun tiga langkah, yaitu mencetak baris, menghitung jumlah, dan mencetak hasilnya, melalui for yang merupakan penulisan ringkas dari rangkaian flatMap dan map. Titik masuk programnya adalah IOApp.Simple, yaitu trait yang menyediakan runtime dan mengeksekusi run setelah program dimulai. Seluruh kode pada objek Jumlah hanya membangun deskripsi, karena IO(...) menyimpan bloknya untuk dieksekusi nanti.
//> using dep org.typelevel::cats-effect:3.6.3
// IO adalah deskripsi program, sedangkan IOApp.Simple yang mengeksekusinya
import cats.effect.IO
import cats.effect.IOApp
object Jumlah extends IOApp.Simple:
// Nilai di bawah hanyalah deskripsi, belum ada yang dijalankan
val deskripsi: IO[Int] = IO((1 to 100).sum)
val run: IO[Unit] =
for
_ <- IO.println("Menjalankan deskripsi")
hasil <- deskripsi
_ <- IO.println(s"Jumlah 1 sampai 100: $hasil")
yield ()# menjalankan program jumlah.scala
scala-cli run jumlah.scalaMenjalankan deskripsi
Jumlah 1 sampai 100: 5050Keluaran hanya berisi dua baris dari IO.println, dan tidak ada satu langkah pun yang dijalankan pada saat objek Jumlah didefinisikan. Apabila deskripsi tidak pernah disusun ke dalam run, penghitungannya tidak pernah terjadi. Nilai deskripsi yang sama juga dapat disusun ulang ke dalam deskripsi lain, dan setiap eksekusinya mengulang penghitungan dari awal, karena deskripsi tidak berubah setelah dibuat.
Jaminan penutupan dengan Resource#
Tipe Resource[IO, A] membungkus perolehan sumber daya beserta penutupnya, dan blok use menjamin penutupan itu terjadi pada setiap jalur, baik jalur sukses maupun jalur kegagalan. Contoh berikut membuka Source dari sebuah berkas dan menyerahkan penutupannya kepada Resource, sehingga tidak ada pemanggilan close yang ditulis manual. Apabila efek di dalam use gagal di tengah eksekusi, penutupan tetap dijalankan sebelum kegagalan dilaporkan ke pemanggil.
import cats.effect.{IO, Resource}
import scala.io.Source
def sumberData(nama: String): Resource[IO, Source] =
Resource.fromAutoCloseable(IO.blocking(Source.fromFile(nama)))
val program: IO[Unit] =
sumberData("data.txt").use(sumber =>
IO.println(sumber.getLines().mkString(", "))
)Bentuk ini adalah versi terstruktur dari disiplin try dan finally, dengan penutupan yang disusun sekali dan digunakan untuk semua jalur. Konstruksi yang sama berlaku untuk sumber daya yang lebih rumit, misalnya koneksi jaringan yang dibagi ke beberapa pengguna, melalui penyusunan Resource yang bersarang. Kewajiban menelusuri setiap jalur kegagalan menjadi tidak relevan, karena jaminannya berada pada konstruksi, bukan pada kedisiplinan pemanggil.
ZIO dengan tiga kanal pada tipenya#
ZIO merepresentasikan efek dengan tipe ZIO[R, E, A] yang memiliki tiga kanal, yaitu lingkungan R yang dibutuhkan efek, error E yang mungkin terjadi, dan hasil A. Alias Task[A] mengunci R pada Any dan E pada Throwable, sehingga Task[A] setara dengan ZIO[Any, Throwable, A]. Titik masuk programnya adalah ZIOAppDefault, yang mengeksekusi nilai run dengan runtime milik ZIO. Contoh berikut membaca teks konfigurasi yang seharusnya berupa angka, dan kegagalan konversinya ditangani pada kanal error tanpa exception yang lolos.
//> using dep dev.zio::zio:2.1.27
// Task[A] adalah ZIO[Any, Throwable, A], efek dengan kanal error Throwable
import zio.*
object Konfigurasi extends ZIOAppDefault:
val run: ZIO[Any, Throwable, Unit] =
for
_ <- Console.printLine("Memeriksa konfigurasi")
angka <- ZIO.attempt("delapan".toInt).orElse(ZIO.succeed(0))
_ <- Console.printLine(s"Nilai konfigurasi: $angka")
yield ()# menjalankan program konfigurasi.scala
scala-cli run konfigurasi.scalaMemeriksa konfigurasi
Nilai konfigurasi: 0ZIO.attempt("delapan".toInt) mengubah kode yang dapat melempar exception menjadi efek dengan kanal error Throwable, dan orElse menggantikan hasil gagalnya dengan ZIO.succeed(0). Karena kanal error merupakan bagian dari tipe, kecenderungan sebuah fungsi untuk gagal terlihat langsung dari tanda tangannya. Metode Console.printLine pada contoh mengembalikan efek dengan kanal error IOException, yang terhitung sebagai Throwable, sehingga seluruh for tetap bertipe ZIO[Any, Throwable, Unit].
| Aspek | cats-effect | ZIO |
|---|---|---|
| Tipe efek | IO[A] | ZIO[R, E, A], dengan alias Task[A] |
| Kanal error | tersedia melalui attempt | bagian eksplisit dari tipe |
| Titik masuk program | IOApp.Simple | ZIOAppDefault |
| Runtime | runtime cats-effect | runtime ZIO |
Kesadaran, bukan penguasaan#
Topik ini memberikan kesadaran, bukan penguasaan. Kode produksi yang menggunakan cats-effect atau ZIO melibatkan hal-hal yang belum disentuh di sini, antara lain fiber sebagai unit konkurensi ringan, primitif bersama seperti Ref dan Queue, streaming, pengujian deterministik yang melibatkan waktu, dan pengelolaan skop yang bertingkat. Menguasainya membutuhkan dokumentasi resmi masing-masing proyek serta latihan pada proyek nyata. Yang dapat dibawa dari topik ini adalah kebiasaan membaca, yaitu mengenali bahwa IO[A] atau ZIO[R, E, A] adalah deskripsi yang belum berjalan, menelusuri susunan komposisinya, dan mencari titik eksekusinya di tepi program. Dengan kebiasaan itu, kode berbasis efek dapat dibaca secara terstruktur alih-alih ditebak baris demi baris.
Ringkasan#
- Effect system merepresentasikan efek samping sebagai nilai deskripsi yang dikomposisikan, lalu dieksekusi sekali di tepi program.
IO[A]pada cats-effect adalah deskripsi yang dieksekusi runtime melaluiIOApp.Simple, danResource.usemenjamin penutupan sumber daya pada jalur sukses maupun gagal.ZIO[R, E, A]menempatkan kanal error pada tipenya, denganTask[A]sebagai alias danZIOAppDefaultsebagai titik masuk.- Topik ini memberikan kesadaran pola, bukan penguasaan yang membutuhkan dokumentasi resmi dan latihan lebih lanjut.
Referensi#
- Cats Effect — Typelevel
— Dokumentasi resmi cats-effect, mencakup model
IO, runtime, dan pengelolaan sumber daya. - Getting started — Typelevel
— Panduan awal resmi untuk menjalankan program pertama berbasis
IOApp. - Dokumentasi ZIO — proyek ZIO
— Dokumentasi resmi ZIO, mencakup model
ZIO[R, E, A]dan runtime-nya. - Reference — proyek ZIO — Referensi resmi konsep inti ZIO, antara lain kanal error, lingkungan, dan konstruksi efek.
- Resource — proyek ZIO
— Halaman resmi pengelolaan sumber daya pada ZIO sebagai pembanding
Resourcemilik cats-effect.