Akses Global Values dengan Reader Monad
Sampai beberapa tahun yang lalu (dan mungkin masih sampai sekarang), komunitas React sempat dibuat hype dengan adanya Redux sebagai state management library. Walaupun pada kenyataannya banyak juga yang hanya menggunakan Redux sebagai wadah untuk global/shared state mereka, dalihnya sih agar terhindar dari props drilling atau menyuplai props secara eksplisit ke setiap level component tree.
Lalu muncul React Context, yang tujuan utamanya persis seperti yang barusan: agar developer terhindar dari props drilling. Dikutip dari dokumentasi resmi React:
Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.
Artikel ini saya tulis lebih karena teringatnya saya pada Reader Monad sebagai salah satu cara untuk menyimpan dan mengatur global values agar deep-nested functions tetap punya akses tanpa props drilling. Persis seperti React Context (bedanya Reader type-safe). Dan saya akan menjelaskannya dengan Purescript.
Apa itu Reader Monad
Seperti yang telah dijelaskan sebelumnya, Reader Monad berfungsi sebagai wadah penyimpanan suatu shared value. Shared value tersebut bisa berupa object connection
yang nantinya dibutuhkan oleh setiap function yang menjalankan database query. Bisa juga berupa kumpulan nilai environment (configuration) yang biasa “disimpan” di file .env
.
Definisi Reader Monad sangat simple:
dimana env
adalah shared value kita dan a
adalah nilai yang dihasilkan dari shared value tersebut, mirip sebuah function env -> a
. Dan memang sejatinya Reader Monad adalah function env -> a
😄. Tapi pembahasan function sebagai Reader kita kesampingkan dulu.
Karena Reader (memiliki instance Monad), kita dapat memanggilnya seperti ini:
Type signature di atas mendeskripsikan bahwa kita telah membuat sebuah Reader yang menerima sebuah object Env
dan menghasilkan Int
. Lalu bagaimana cara menyuplai object Env
dan mendapatkan nilai 4005? Dengan function runReader
:
runReader
menerima Reader di argument pertamanya lalu disuplai dengan shared value env
di argument kedua sehingga menghasilkan nilai a
. Namun sampai contoh barusan kita belum juga menggunakan Env
atau shared value yang kita suplai. Lalu bagaimana cara mendapatkannya? Dengan method ask
!
Contoh yang lebih real world:
Ketika ask
dipanggil, ia akan mengembalikan environment yang nantinya disuplai. Jadi kalau ingin mendapatkan environment, ask for it 😉.
Kurang lebih inilah yang dimaksud dengan Reader Monad beserta penggunaan dasarnya. Lihat bagaimana dalam pemanggilan function fetchAuthedUser
kita tidak pernah benar-benar secara eksplisit melempar object Env
ke function argument-nya. Begitu pula dalam pemanggilan fetchArticles
, tidak ada explicit passing object Env
. Semua “disembunyikan” lewat Reader.
Update Global Value?
Lumrahnya penggunaan Reader Monad hanyalah untuk dibaca value-nya, sesuai dengan namanya, Reader. Namun bukan berarti Reader tidak bisa diperlakukan seperti global setter. Kita tetap bisa meng-update environment yang tersimpan di Reader. Dengan sedikit hack: Ref
.
Ref
adalah struktur data yang mutable di Purescript. Segala operasi yang berkaitan dengannya harus dijalankan di bawah payung Effect Monad, karena memang nature-nya yang tidak pure.
Contoh kecil adalah ketika aplikasi baru diakses lewat browser dan user belum melakukan login. Sedangkan object user ini perlu diakses di banyak tempat. Kita tetap bisa menempatkan object user ini sebagai shared value di Reader.
Nanti di bagian aplikasi lain, ketika user telah ter-autentikasi, barulah currentUser
dapat di-update dan dibaca oleh function lain
Wrap up
Kesimpulannya: Reder Monad sangat cocok untuk meneruskan informasi/value secara implisit untuk menghasilkan sebuah komputasi. Setiap kali kita memiliki sebuah value yang dibutuhkan di banyak tempat namun ingin menghasilkan hasil komputasi yang berbeda-beda, maka Reader Monad bisa menjadi salah satu solusi.
Saya harap tulisan ini dapat membantu dalam memahami motivasi di balik Reader Monad beserta penggunaannya. Terima kasih.