Sembunyikan State-mu dengan State Monad
Ceritanya lagi buat aplikasi bank kecil-kecilan.
Argument function withdraw
adalah jumlah yang ingin kita ambil dan nilai kembaliannya kita asumsikan sebagai uang yang keluar dari mesin ATM.
Namun function tersebut kurang real world. Untuk melakukan penarikan uang, mesin harus menghitung beberapa faktor yang telah ditentukan oleh pihak bank seperti jumlah penarikan, biaya penarikan, saldo, dll. Sedangkan tak ada pengecekan apapun di function tersebut. Mungkin harus kita buat versi yang lebih baik.
Sekarang function withdraw
akan cek terlebih dahulu apakah sisa saldo setelah penarikan masih lebih dari 50. Jika demikian, penarikan berhasil. Namun sayangnya sisa saldo setelah penarikan tidak berubah sama sekali. Mungkin perlu diiterasi lagi supaya mengembalikan dua hal sekaligus: jumlah yang keluar dari ATM, dan hasil saldo akhir setelah transaksi.
Dengan begini, nilai balance
juga ikut dikomputasi setiap kali melakukan penarikan dan hasilnya dikembalikan ke caller.
Pattern yang langsung terlihat dari penggunaan fungsi withdraw
adalah nilai balance
yang dioper-oper secara eksplisit dari satu function ke function lainnya, yang nilainya berpotensi berubah di setiap pemanggilan withdraw
. Nilai yang berubah-ubah ini bisa kita namakan dengan State.
Sehingga, dalam domain studi kasus “bank” ini, saldo atau balance
adalah State yang harus kita maintain.
Balik ke permasalahan code, walaupun dalam banyak hal code mungkin akan lebih mudah dicerna dengan explicit passing, namun dalam hal ini akan sangat tidak readable bila harus terus melakukan destructuring Tuple dan menyuplai State ke function berikutnya.
State monad bisa membantu menyembunyikannya.
Definisi State Monad
Sebetulnya function withdraw
sudah menyerupai pattern State monad. Perhatikan type signature-nya dan fokus pada Balance, karena ia adalah state yang ingin kita maintain.
Jadi sebenarnya State monad hanyalah sebuah function yang mengambil state dan mengembalikan state berikutnya, dibarengi dengan intermediate value (biasanya berupa hasil komputasi yang bergantung pada state).
Refactor
Mari refactor function withdraw
menggunakan State monad.
Ada dua method State monad yang muncul di sini: get
yang mengambil nilai state terbaru, dan put
yang meng-overwrite nilai state.
Dengan style ini, function transactions
menjadi lebih singkat dan kita tak perlu lagi passing state secara eksplisit.
Dan untuk menjalankannya, kita hanya perlu memanggil runState
(atau evalState
atau execState
, tergantung kebutuhan) dan menyuplainya dengan initial state.
Proses modifikasi dan passing state ini hanya terjadi di dalam operasi monad (ketika memanggil runState
), menjaga program tetap pure dan bebas dari side effect. Mungkin ini salah satu perbedaan yang cukup mencolok dibandingkan dengan pendekatan OOP yang menggunakan class properties sebagai state variable. Atau pada pendekatan prosedural yang memanfaatkan mutable variable untuk menyimpan perubahan state.
All in all, State monad memberikan jalan alternatif yang pure untuk melakukan komputasi yang “stateful”. Menambahkan state pada function a -> b
cukup dengan mengubahnya menjadi a -> State s b
, dan kita sudah mendapatkan function get
, put
dan modify
secara gratis.
Semoga bermanfaat.