Cats Effect telah menjadi semacam “Aliran Reaktif” untuk dunia Scala fungsional, memungkinkan Anda untuk menggabungkan seluruh ekosistem perpustakaan yang beragam menjadi satu.
Banyak perpustakaan yang luar biasa: http4s, fs2, doobie - diimplementasikan hanya berdasarkan kelas tipe dari Cats Effect. Dan pustaka seperti ZIO dan Monix, pada gilirannya, menyediakan instance dari kelas tipe ini untuk tipe efeknya. Meskipun ada beberapa masalah yang akan diperbaiki pada versi 3.0, Cats Effect membantu banyak kontributor open source untuk secara organik mendukung seluruh ekosistem fungsional bahasa Scala. Pengembang yang menggunakan Efek Kucing dihadapkan pada pilihan yang sulit: penerapan efek yang akan digunakan untuk aplikasi mereka.
Hari ini ada tiga alternatif:
- IO Kucing, implementasi referensi;
- Monix, tipe data Tugas dan reaktivitasnya dalam kode;
- ZIO, tipe data ZIO dan cakupan lintas-threading-nya.
Dalam posting ini saya akan mencoba membuktikan kepada Anda bahwa untuk membuat aplikasi Anda menggunakan Cats Effect, ZIO adalah pilihan yang baik dengan solusi desain dan kemampuan yang sangat berbeda dari implementasi referensi di Cats IO.
1. Arsitektur MTL / Tagless-Final yang lebih baik
MTL (Monad Transformers Library) adalah gaya pemrograman yang fungsinya polimorfik berdasarkan jenis efeknya dan mengekspresikan kebutuhannya melalui “type class constraint”. Di Scala, ini sering disebut gaya tagless-final (meskipun bukan hal yang sama), terutama ketika kelas jenis tidak memiliki undang-undang.
Telah diketahui secara umum bahwa tidak mungkin untuk mendefinisikan instance global untuk kelas tipe MTL klasik seperti Writer and State, serta untuk tipe efek seperti Kucing IO. Masalahnya adalah bahwa instance dari kelas tipe ini untuk jenis efek ini memerlukan akses ke keadaan bisa berubah, yang tidak dapat dibuat secara global, karena membuat keadaan bisa berubah juga merupakan efek.
Untuk kinerja terbaik, bagaimanapun, penting untuk menghindari "transformator monad" dan memberikan implementasi Write dan State secara langsung, di atas jenis efek utama.
Untuk mencapai ini, pemrogram Scala menggunakan trik: mereka membuat contoh (tetapi bersih) di tingkat atas program mereka dengan efek dan kemudian memberikan mereka lebih lanjut dalam program sebagai implikasi lokal:
Ref.make[AppState](initialAppState).flatMap(ref => implicit val monadState = new MonadState[Task, AppState] { def get: Task[AppState] = ref.get def set(s: AppState): Task[Unit] = ref.set(s).unit } myProgram )
Terlepas dari kenyataan bahwa trik semacam itu berguna, itu masih "penopang". Dalam dunia yang ideal, semua instance kelas tipe bisa koheren (satu instance per tipe), dan tidak dibuat secara lokal, menghasilkan efek, dan kemudian secara ajaib membungkus diri dalam nilai implisit untuk digunakan dengan metode selanjutnya.
Fitur hebat dari MTL / tagless-final adalah Anda dapat langsung mendefinisikan sebagian besar contoh di atas tipe data ZIO menggunakan lingkungan ZIO.
Berikut adalah salah satu cara untuk membuat definisi MonadState global untuk tipe data ZIO:
trait State[S] { def state: Ref[S] } implicit def ZIOMonadState[S, R <: State[S], E]: MonadState[ZIO[R, E, ?], S] = new MonadState[ZIO[R, E, ?], S] { def get: ZIO[R, E, S] = ZIO.accessM(_.state.get) def set(s: S): ZIO[R, E, Unit] = ZIO.accessM(_.state.set(s).unit) }
Sebuah instance sekarang didefinisikan secara global untuk setiap lingkungan yang mendukung setidaknya
State[S]
.
Demikian pula untuk
FunctorListen
, atau dikenal sebagai
MonadWriter
:
trait Writer[W] { def writer: Ref[W] } implicit def ZIOFunctorListen[W: Semigroup, R <: Writer[W], E]: FunctorListen[ZIO[R, E, ?], W] = new FunctorListen[ZIO[R, E, ?], W] { def listen[A](fa: ZIO[R, E, A]): ZIO[R, E, (A, W)] = ZIO.accessM(_.state.get.flatMap(w => fa.map(a => a -> w))) def tell(w: W): ZIO[R, E, W] = ZIO.accessM(_.state.update(_ |+| w).unit) }
Dan tentu saja, kita dapat melakukan hal yang sama untuk
MonadError
:
implicit def ZIOMonadError[R, E]: MonadError[ZIO[R, E, ?], E] = new MonadError[ZIO[R, E, ?], E]{ def handleErrorWith[A](fa: ZIO[R, E, A])(f: E => ZIO[R, E, A]): ZIO[R, E, A] = fa catchAll f def raiseError[A](e: E): ZIO[R, E, A] = ZIO.fail(e) }
Teknik ini mudah diterapkan pada kelas tipe lain, termasuk kelas tipe tag-final, yang instansinya mungkin memerlukan efek hasil (perubahan, konfigurasi), fungsi pengujian yang menghasilkan efek (menggabungkan efek lingkungan dengan tagless-final), atau hal lain yang mudah diakses dari lingkungan .
Tidak ada lagi transformasi monadik yang lambat! Katakanlah “tidak” untuk menciptakan efek ketika menginisialisasi instance kelas kelas, ke implikasi lokal. Tidak perlu lagi kruk. Perendaman langsung dalam pemrograman fungsional murni.
2. Menyimpan sumber daya untuk manusia biasa
Salah satu fitur pertama ZIO adalah interraption - kemampuan runtime ZIO untuk secara instan menginterupsi setiap efek yang dapat dieksekusi dan dijamin untuk membebaskan semua sumber daya. Implementasi kasar fitur ini mengenai Cats IO.
Haskell menyebut pengecualian fungsi seperti itu sebagai sinkronisasi, yang memungkinkan Anda untuk membuat dan menggunakan latensi secara efisien, operasi paralel dan kompetitif yang efisien, dan perhitungan yang optimal secara global. Gangguan seperti itu tidak hanya membawa manfaat besar, tetapi juga menimbulkan tugas rumit di bidang mendukung akses yang aman ke sumber daya.
Pemrogram digunakan untuk melacak kesalahan dalam program melalui analisis sederhana. Ini juga dapat dilakukan dengan ZIO, yang menggunakan sistem tipe untuk membantu mendeteksi kesalahan. Tetapi gangguan adalah hal lain. Efek yang dibuat dari banyak efek lain dapat terganggu di perbatasan mana pun.
Pertimbangkan efek berikut:
for { handle <- openFile(file) data <- readFile(handle) _ <- closeFile(handle) } yield data
Sebagian besar pengembang tidak akan terkejut dengan skenario ini:
closeFile
tidak akan dieksekusi jika
readFile
lumpuh. Untungnya, sistem efek memiliki
ensuring
(
guarantee
dalam Efek Kucing) yang memungkinkan Anda untuk menambahkan penangan akhir ke efek finalizer, mirip dengan akhirnya.
Jadi, masalah utama dari kode di atas dapat dengan mudah diselesaikan:
for { handle <- openFile(file) data <- readFile(handle).ensuring(closeFile(handle)) } yield ()
Sekarang efeknya telah menjadi "tahan jatuh", dalam arti bahwa jika
readFile
rusak, file akan tetap ditutup. Dan jika
readFile
berhasil, file tersebut juga akan ditutup. Dalam semua kasus, file akan ditutup.
Tapi masih belum sepenuhnya. Gangguan berarti bahwa efeknya dapat terganggu di mana-mana, bahkan antara
openFile
dan
openFile
. Jika ini terjadi, file yang terbuka tidak akan ditutup dan kebocoran sumber daya akan terjadi.
Pola mendapatkan dan melepaskan sumber daya begitu luas sehingga ZIO memperkenalkan operator braket, yang juga muncul dalam Cats Effect 1.0. Pernyataan Bracket melindungi terhadap gangguan: jika sumber daya diterima dengan sukses, maka pelepasan akan terjadi bahkan jika efek menggunakan sumber daya terganggu. Selanjutnya, baik penerimaan maupun pelepasan sumber daya tidak dapat terganggu, sehingga memberikan jaminan keamanan sumber daya.
Menggunakan braket, contoh di atas akan terlihat seperti ini:
openFile(file).bracket(closeFile(_))(readFile(_))
Sayangnya, braket merangkum hanya satu pola konsumsi sumber daya (cukup umum). Ada banyak lainnya, terutama dengan struktur data kompetitif, akses yang harus dapat diakses untuk interupsi, jika tidak, kebocoran mungkin terjadi.
Secara umum, semua pekerjaan interupsi bermuara pada dua hal utama:
- mencegah interupsi di beberapa area yang mungkin terganggu;
- memungkinkan gangguan di area yang mungkin membeku.
ZIO memiliki kemampuan untuk mengimplementasikan keduanya. Misalnya, kita dapat mengembangkan versi braket sendiri menggunakan abstraksi ZIO tingkat rendah:
ZIO.uninterruptible { for { a <- acquire exit <- ZIO.interruptible(use(a)) .run.flatMap(exit => release(a, exit) .const(exit)) b <- ZIO.done(exit) } yield b }
Dalam kode ini,
use(a)
adalah satu-satunya bagian yang dapat diinterupsi. Kode di sekitarnya menjamin eksekusi
release
dalam hal apa pun.
Kapan saja, Anda dapat memeriksa apakah ada peluang untuk gangguan. Untuk ini, hanya dua operasi primitif yang diperlukan (semua sisanya berasal dari mereka).
Model interupsi fitur lengkap komposisi ini memungkinkan Anda untuk menerapkan tidak hanya implementasi braket sederhana, tetapi juga menerapkan skenario lain dalam manajemen sumber daya, di mana keseimbangan ditemukan antara kelebihan dan kekurangan interupsi.
IO Kucing hanya menyediakan satu operasi untuk mengendalikan interupsi: kombinator yang tidak dapat dibatalkan. Itu membuat seluruh blok kode tidak terganggu. Meskipun operasi ini jarang digunakan, ini dapat menyebabkan kebocoran sumber daya atau kunci.
Pada saat yang sama, ternyata Anda dapat mendefinisikan primitif di dalam Cats IO, yang memungkinkan Anda mencapai kontrol lebih besar atas interupsi. Implementasi Fabio Labella yang sangat rumit ternyata sangat lambat.
ZIO memungkinkan Anda untuk menulis kode dengan interupsi, beroperasi pada tingkat tinggi dengan pernyataan gabungan deklaratif, dan tidak memaksa Anda untuk memilih antara kerumitan parah yang dikombinasikan dengan kinerja rendah dan pemblokiran kebocoran.
Selain itu, Memori Transaksional Perangkat Lunak yang baru ditambahkan dalam ZIO memungkinkan pengguna untuk secara deklaratif menulis struktur data dan kode yang secara otomatis tidak sinkron, kompetitif, dan memungkinkan gangguan.
3. Finalizers yang Dijamin
Blok coba / akhirnya dalam banyak bahasa pemrograman memberikan jaminan yang diperlukan untuk menulis kode sinkron tanpa membocorkan sumber daya.
Secara khusus, blok ini menjamin yang berikut: jika blok try memulai eksekusi, maka blok akhirnya akan mengeksekusi ketika try berhenti.
Garansi ini berlaku untuk:
- ada blok "coba / akhirnya" bersarang;
- ada kesalahan dalam "coba blok";
- ada kesalahan di blok akhirnya bersarang.
Operasi “memastikan” ZIO dapat digunakan seperti mencoba / akhirnya:
val effect2 = effect.ensuring(cleanup)
ZIO memberikan jaminan berikut untuk "effect.ensuring (finalizer)": jika "effect" mulai dieksekusi, maka "finalizer" akan memulai eksekusi ketika "effect" berhenti.
Seperti coba / akhirnya, jaminan ini tetap dalam kasus berikut:
- Ada komposisi "memastikan" bersarang;
- ada kesalahan dalam "efek";
- ada kesalahan dalam "finalizer" bersarang.
Selain itu, jaminan dipertahankan bahkan jika efeknya terganggu (jaminan pada "braket" serupa, pada kenyataannya, "braket" diterapkan pada "memastikan").
Tipe data IO Kucing memberikan jaminan lain yang lebih lemah. Untuk "effect.guarantee (finalizer)", itu dilemahkan sebagai berikut: jika "effect" mulai dieksekusi, "finalizer" akan memulai eksekusi ketika "effect" berhenti, jika efek masalah tidak dimasukkan ke dalam "effect".
Jaminan yang lebih lemah juga ditemukan dalam penerapan "braket" di Kucing IO.
Untuk mendapatkan sumber daya bocor, cukup gunakan efek yang digunakan di dalam efek "jaminan" atau "bracket.use", buat dengan sesuatu seperti ini:
Ketika bigTrouble dimasukkan dengan cara ini ke efek lain, efeknya menjadi tidak terganggu - tidak ada "finalizers" yang ditetapkan melalui "jaminan", atau pembersihan sumber daya melalui "braket" tidak akan dieksekusi. Semua ini mengarah pada pengurasan sumber daya, bahkan ketika ada "finalizer" di blok.
Misalnya, "finalizer" dalam kode berikut tidak akan pernah memulai eksekusi:
(IO.unit >> bigTrouble).guarantee(IO(println("Won't be executed!!!«)))
Ketika mengevaluasi kode tanpa mempertimbangkan konteks global, tidak mungkin untuk menentukan apakah suatu efek, seperti "bigTrouble", akan dimasukkan di mana saja dalam efek "penggunaan" operasi "braket" atau di dalam blok "finalizer".
Karenanya, Anda tidak akan dapat mengetahui apakah program IO Kucing akan bekerja dengan kebocoran sumber daya atau kehilangan blok "penyelesai" tanpa mengevaluasi keseluruhan program. Seluruh program hanya dapat dievaluasi secara manual, dan proses ini selalu disertai dengan kesalahan yang tidak dapat diverifikasi oleh kompiler. Selain itu, proses ini harus diulang setiap kali terjadi perubahan penting pada kode.
ZIO memiliki implementasi kustom "jaminan" dari Cats Effect, "guaranteeCase" dan "bracket". Implementasi menggunakan semantik ZIO asli (bukan Semantik Kucing IO), yang memungkinkan kami untuk mengevaluasi kemungkinan masalah dengan kebocoran sumber daya di sini dan sekarang, mengetahui bahwa dalam semua situasi, finalizer akan diluncurkan dan sumber daya akan dibebaskan.
4. Pergantian yang stabil
Cats Effect memiliki metode "evalOn" dari "ContextShift", yang memungkinkan Anda untuk memindahkan eksekusi beberapa kode ke konteks eksekusi lainnya.
Ini berguna karena sejumlah alasan:
- banyak perpustakaan klien memaksa Anda untuk melakukan beberapa pekerjaan di kumpulan utangnya;
- Perpustakaan UI memerlukan beberapa pembaruan untuk terjadi di utas UI;
- beberapa efek memerlukan isolasi pada kolam ulir yang disesuaikan dengan fitur spesifiknya.
Operasi "EvalOn" mengeksekusi efek di mana ia harus dijalankan, dan kemudian kembali ke konteks eksekusi asli. Sebagai contoh:
cs.evalOn(kafkaContext)(kafkaEffect)
Catatan: Kucing IO memiliki konstruksi "shift" yang serupa, yang memungkinkan Anda untuk beralih ke konteks yang berbeda tanpa kembali, tetapi dalam praktiknya, perilaku ini jarang diperlukan, jadi "evalOn" lebih disukai.
Implementasi ZIO dari "evalOn" (dibuat pada "kunci" ZIO primitif) memberikan jaminan yang diperlukan untuk secara unik memahami di mana efek bekerja - efeknya akan selalu dieksekusi dalam konteks tertentu.
IO Kucing memiliki jaminan yang berbeda dan lebih lemah - efeknya akan dieksekusi dalam konteks tertentu hingga operasi asinkron pertama atau pengalihan internal.
Mempertimbangkan sepotong kecil kode, tidak mungkin untuk mengetahui dengan pasti apakah efek asinkron (atau pengalihan bersarang) akan dibangun ke dalam efek yang akan beralih, karena asinkron tidak ditampilkan dalam tipe.
Oleh karena itu, seperti dalam hal keamanan sumber daya, untuk memahami di mana efek IO Kucing akan diluncurkan, perlu mempelajari seluruh program. Dalam praktiknya, dan dari pengalaman saya, pengguna Cats IO terkejut ketika, ketika menggunakan "evalOn" dalam satu konteks, kemudian ditemukan bahwa sebagian besar efeknya secara tidak sengaja dilakukan di lain.
ZIO memungkinkan Anda untuk menentukan di mana efek harus dipicu, dan percaya bahwa itu akan terjadi dalam semua kasus, tidak peduli bagaimana efek dibangun ke efek lainnya.
5. Keamanan pesan kesalahan
Setiap efek yang mendukung konkurensi, konkurensi, atau akses aman ke sumber daya akan mengalami model kesalahan linier: secara umum, tidak semua kesalahan dapat disimpan.
Ini berlaku untuk `Throwable`, tipe kesalahan tetap yang ada pada Kucing IO, dan tipe kesalahan polimorfik yang didukung oleh ZIO.
Contoh situasi dengan beberapa kesalahan satu kali:
- Finalizer melempar pengecualian;
- dua (jatuh) efek digabungkan dalam eksekusi paralel;
- dua efek (jatuh) dalam kondisi balap;
- efek terputus turun sebelum meninggalkan bagian terlindung dari gangguan.
Karena tidak semua kesalahan disimpan, ZIO menyediakan struktur data "Penyebab [E]" berdasarkan semiring gratis (abstraksi dari aljabar abstrak, pengetahuannya tidak seharusnya ada di sini), yang memungkinkan menghubungkan kesalahan serial dan paralel untuk semua jenis kesalahan. Selama semua operasi (termasuk pembersihan untuk efek yang jatuh atau terganggu), ZIO mengumpulkan kesalahan ke dalam struktur data "Penyebab [E]". Struktur data ini tersedia kapan saja. Sebagai hasilnya, ZIO selalu menyimpan semua kesalahan: mereka selalu tersedia, mereka dapat dicatat, dipelajari, dan ditransformasikan sesuai dengan persyaratan bisnis.
IO Kucing memilih model dengan informasi kesalahan yang hilang. Sementara ZIO akan menghubungkan dua kesalahan melalui Penyebab [E], Kucing IO akan "kehilangan" salah satu pesan kesalahan, misalnya, dengan memanggil "e.printStackTrace ()" pada kesalahan yang terjadi.
Misalnya, kesalahan dalam "finalizer" dalam kode ini akan hilang.
IO.raiseError(new Error("Error 1")).guarantee(IO.raiseError(new Error("Error 2«)))
Pendekatan ini untuk melacak kesalahan berarti bahwa Anda tidak dapat menemukan dan memproses seluruh spektrum kesalahan yang terjadi secara lokal karena kombinasi efek. ZIO memungkinkan Anda untuk menggunakan jenis kesalahan apa pun, termasuk "Throwable" (atau subtipe lebih spesifik seperti "IOExceptio" atau hierarki pengecualian kustom lainnya), memastikan bahwa tidak ada kesalahan yang hilang selama eksekusi program.
6. Asynchrony tanpa deadlock
Baik ZIO dan Cats IO menyediakan konstruktor yang memungkinkan Anda untuk mengambil kode dengan panggilan balik dan membungkusnya dengan efektif
Fitur ini disediakan melalui kelas pipa Async di Cats Effect:
val effect: Task[Data] = Async[Task].async(k => getDataWithCallbacks( onSuccess = v => k(Right(v)), onFailure = e => k(Left(e)) ))
Ini menciptakan efek asinkron, yang, ketika dieksekusi, akan menunggu sampai nilai muncul, dan kemudian melanjutkan, dan semua ini akan jelas bagi pengguna efek. Oleh karena itu, pemrograman fungsional sangat menarik untuk mengembangkan kode asinkron.
Perhatikan bahwa segera setelah kode panggilan balik berubah menjadi efek, fungsi panggilan balik (ini disebut `k`) dipanggil. Fungsi panggilan balik ini keluar dengan nilai sukses / kesalahan. Ketika fungsi panggilan balik ini dipanggil, eksekusi efek (yang sebelumnya dijeda) dilanjutkan.
ZIO menjamin bahwa efek akan melanjutkan eksekusi pada kumpulan thread runtime jika efek tidak ditugaskan ke konteks khusus tertentu, atau ke konteks lain di mana efek itu dilampirkan.
IO Kucing melanjutkan efeknya pada utas panggilan balik. Perbedaan antara opsi-opsi ini cukup dalam: utas yang menyebabkan panggilan balik tidak mengharapkan kode panggilan balik dijalankan selamanya, tetapi hanya memungkinkan sedikit penundaan sebelum kontrol kembali. Di sisi lain, Kucing IO sama sekali tidak memberikan jaminan seperti itu: utas panggilan, panggilan balik peluncuran, dapat membeku, menunggu waktu yang tidak terbatas ketika kontrol eksekusi kembali.
Versi sebelumnya dari struktur data kompetitif dalam Cats Effect ("Deferred", "Semaphore") melanjutkan efek yang tidak mengembalikan kontrol eksekusi ke utas panggilan. Akibatnya, masalah yang terkait dengan kebuntuan dan penjadwalan eksekusi yang rusak ditemukan di dalamnya. Meskipun semua masalah ini telah ditemukan, mereka hanya diperbaiki untuk struktur data kompetitif di Cats Effect.
Kode pengguna yang menggunakan pendekatan yang sama seperti pada Kucing IO akan masuk ke masalah seperti itu, karena tugas-tugas seperti itu tidak deterministik, kesalahan hanya bisa terjadi sangat jarang di runtime, membuat debugging dan deteksi masalah menjadi proses yang sulit.
ZIO memberikan perlindungan kebuntuan dan penjadwal tugas normal di luar kotak, dan juga membuat pengguna secara eksplisit memilih perilaku Kucing IO (misalnya, menggunakan "unsafeRun" pada "Janji", yang berakhir dengan efek asinkron yang dilanjutkan).
Meskipun tidak ada solusi yang cocok untuk semua kasus, dan ZIO dan Kucing IO memberikan fleksibilitas yang cukup untuk menyelesaikan semua situasi (dengan cara yang berbeda), memilih ZIO berarti menggunakan "Async" tanpa kekhawatiran dan memaksa Anda untuk memasukkan kode masalah ke "unsafeRun", yang diketahui menyebabkan kebuntuan
7. Kompatibel dengan Masa Depan
Menggunakan "Masa Depan" dari perpustakaan standar Scala adalah kenyataan untuk sejumlah besar basis kode. ZIO hadir dengan metode "fromFuture", yang menyediakan konteks eksekusi yang sudah jadi:
ZIO.fromFuture(implicit ec =>
Ketika metode ini digunakan untuk membungkus masa depan dalam efek, ZIO dapat mengatur di mana masa depan akan dieksekusi, dan metode lain, seperti evalOn, akan mentransfer masa depan dengan benar ke konteks eksekusi yang diinginkan. Cats IO menerima "Future", yang dibuat dengan "ExecutionContext" eksternal. Ini berarti bahwa IO Kucing tidak dapat memindahkan eksekusi Masa Depan sesuai dengan persyaratan metode evalOn atau shift. Selain itu, ini membebani pengguna dengan menentukan konteks eksekusi untuk Masa Depan, yang berarti seleksi yang sempit dan lingkungan yang terpisah.
Karena ExecutionContext yang diberikan dapat diabaikan, ZIO dapat direpresentasikan sebagai jumlah fitur IO Kucing, yang menjamin interaksi yang lebih lancar dan lebih akurat dengan Future dalam kasus umum, tetapi masih ada pengecualian.
8. Memblokir IO
Seperti yang ditunjukkan pada artikel “
Thread Pool. Praktik terbaik dengan ZIO ”, untuk aplikasi server, setidaknya dua kumpulan terpisah diperlukan untuk efisiensi maksimum:
- kumpulan tetap untuk efek CPU / asinkron;
- dinamis, dengan kemungkinan meningkatkan jumlah utas pemblokiran.
Keputusan untuk menjalankan semua efek pada kumpulan utas tetap suatu hari akan menyebabkan kebuntuan, sementara memicu semua efek pada kumpulan dinamis dapat menyebabkan hilangnya kinerja.
Pada JVM, ZIO menyediakan dua operasi yang mendukung efek pemblokiran:
- "Blocking (effect" operator, yang mengalihkan eksekusi efek tertentu dalam kumpulan thread yang memiliki preset yang baik yang dapat diubah jika diinginkan);
- «effectBlocking(effect)» , , .
, , , «blocking». , - , , «effectBlocking» , ZIO ( ).
Cats IO , . , «blocking», «evalOn», , , .
( ZIO) (, ), .
9.
, Scala, :
- «ReaderT»/ «Kleisli», ;
- «EitherT», ( «OptionT», «EitherT» «Unit» ).
, (, http4s «Kleisli» «OptionT»). («effect totation»), ZIO «reader» «typed error» ZIO. «reader» «typed error» , ZIO , . , «Task[A]», «reader» «typed errors».
ZIO () - . , ZIO , .
Cats IO . , , «reader» «typed errors» «state», «writer» , .
ZIO 8 Cats IO . , Scala .
10.
ZIO , , . , Scala, .
ZIO 2000 , «typed errors» , — 375 . Scala , . , , .
:
. , - , .
- . , . ZIO . Cats IO , , ZIO ( , ).
11.
ZIO , , - .
- , : «ZIO. succeed» «Applicative[F].pure», «zip» «Apply[F].product», «ZIO.foreach» «Traverse[F].traverse».
- (Cats, Cats Effect, Scalaz ).
- , ( «Runtime», Cats Effect - Cats Effect). — Cats IO.
- .
- . : "zip"/"zipPar", "ZIO.foreach"/"ZIO.foreachPar", "ZIO.succeed"/"ZIO.succeedLazy«.
- , «». ZIO IDE.
- Scala ZIO : «ZIO.fromFuture», «ZIO.fromOption», «ZIO.fromEither», «ZIO.fromTry».
- «».
, Scala, , ZIO , , , ZIO, . Cats IO , Cats.
, , , ( , , ).
12.
ZIO — - , .
:
- , «Ref», «Promise», «Queue», «Semaphore» «Stream» //;
- STM, , , ;
- «Schedule», ;
- «Clock», «Random», «Console» «System» , ;
- , .
- Cats IO . Cats IO , ( ) .
Kesimpulan
Cats Effect Scala-, , .
, Cats Effect, , Cats Effect : Cats IO, Monix, Zio.
, . , , , : ZIO Cats Effect .
Scala — . , Scala. ScalaConf , 18 , John A De Goes .