Functional Programming: Mengenal Kleisli
Kleisli itu cuma function composition buat Monad
Dulu, ketika pertama kali mendengar istilah Kleisli, yang ada dalam pikiran saya adalah mantan saya yang unyu ini:
Eh tapi ternyata dia lebih memilih pria lain dan malah jadi bintang film. Da saya mah apa atuh, cuma setitik upil kering di padang pasir…
*Krik krik* Balik ke topik. Bagi kamu-kamu yang dalemin functional programming, pasti tau dong apa itu function composition.
Terus juga pasti tau dong apa itu Monad.
Nah kan udah tau nih. Sekarang saya mau ngenalin istilah baru, Kleisli. Ga usah banyak babibu, langsung aja ke kodingan.
Monad Function Composition
Kayak yang sebelum-sebelumnya, saya berikan contoh data yang familiar saja:
const person = {
name: 'Munir',
age: 59,
child: {
name: 'Jihad',
age: 24
}
}
lalu ada soal pelajaran Kimia yang berbunyi: Berapakah nilai umur anak orang dibagi 2? Yang kita harap jawabannya adalah 12. Untuk menjawab soal tersebut, kita buat dulu fungsi-fungsi dasarnya:
Dan, dengan menggunakan Monad, kita bisa membuat solusinya dengan method bind
const res = Maybe(data)
.bind(getChild)
.bind(getAge)
.bind(safeDivisionBy(2))console.log(res)
// => Maybe(12)
Pertanyaannya
Bagaimana kalau kita mau buat sebuah function getHalfOfChildsAge
karena (misalkan) akan digunakan di banyak tempat? Akankah seperti ini?
const getHalfOfChildsAge = compose(
safeDivisionBy(2),
getAge,
getChild
)console.log(getHalfOfChildsAge(person)) // atau
console.log(getHalfOfChildsAge(Maybe.of(person)))// TETOOT, keduanya tak bisa
Analisa
Mari kita lihat fungsi getHalfOfChildsAge
dari type signature-nya dulu.
Nah dari situ aja udah keliatan bentrok. Fungsi getAge
expect Person
tapi malah dapet Maybe Person
dan begitu juga safeDivisionBy
yang expect Number
tapi malah dapet Maybe Number
. Gak match. Terus gimana solusinya?
Introduce The Mother of Dragon, The Kleisli!
Kita mulai dari naming dulu ya, karena tau sendiri, “Naming things is hard in Computer Science”. Nah, karena Kleisli ini fungsinya mirip-mirip dengan compose
, kita sepakati saja kita akan buat sebuah fungsi bernama composeK
, alias compose Kleisli. Yang nantinya bisa dipake kayak gini.
const getHalfOfChildsAge = composeK(
safeDivisionBy(2),
getAge,
getChild
)console.log(getHalfOfChildsAge(person))
// => Just(12)
Implementasinya gimana pakde?
Sama seperti compose
, kita gunakan reduceRight
, tapi dengan pemanggilan bind di setiap iterasinya.
Simple banget pakde
Ya memang begitu.
Bingung pakde
Gini-gini. Kita pake kasus yang di atas aja ya. Mulai dari pertanyaan pertama. Kenapa ada const lastArg = fns.pop
? Jawabannya karena saya ndak tau implementasi Monad yang mau dipake; bisa Maybe, Either, IO, atau yang lainnya. Makanya lastArg
itu isinya adalah getChild
dan lastArg(init)
isinya getChild(person)
. Sampai step ini, kita jadi tahu bahwa nilai yang di-pass ke dalam fungsi reduce akan bernotasi Maybe Person
, dan juga kita jadi tahu ketika melakukan acc.bind(x)
, variable acc
ini merupakan instance dari Maybe, yang pada akhirnya fungsi fns.reduceRight
akan mengembalikan Maybe juga.
Pertanyaan kedua: kenapa pake bind
di dalam fungsi reduce-nya? Coba kita recall lagi problem dengan compose
tadi. Fungsi getAge
expect Person
, sedangkan dapetnya malah Maybe Person
dari fungsi getChild
. Jadi, harus ada suatu mekanisme (fungsi) yang bisa “membuka” Maybe Person
menjadi Person
. Fungsi apakah itu? Tadaa, fungsi bind
pada Monad! Ingat kan notasi seperti ini?
M[A].bind(fn: A => M[B]): M[B]
in our case,
Maybe[Person].bind(fn: Person => Maybe[Number]): Maybe[Number]
Nah disini jadi terlihat kan fungsi bind adalah pihak yang bertugas membuka bungkus Maybe[Person]
menjadi Person
(saya buat bold lho itu). Lalu jika diperhatikan lebih dalam lagi, kita bisa lihat notasi fn
di atas, Person => Maybe[Number]
atau yang juga bisa dituliskan sebagai Person → Maybe Number
, merupakan type signature yang dimiliki oleh fungsi getAge
. Cucok!
Gampang kan ya? 😙
Kesimpulan
Sahabat-sahabatku yang super,
Kleisli adalah salah satu cara melakukan function composition untuk fungsi-fungsi yang mengembalikan nilai ber-instance Monad
Itu. 👴🏼👉🏼
Penutup
Semoga temen-temen (including me) tetep terus semangat belajar konsep-konsep dari Functional Programming. Jangan terlalu pusing terbawa sama jargon-jargonnya; Monad, Functor, Monoid, Semigroup, Endofunctor, Applicative, Kleisli, Compose, Currying, Lift, Morphism, Type Signature, Algebraic Data Type, bla bla bla bla. Doakan supaya kita diperbanyak sama tutorial-tutorial bagus berbahasa Indonesia. Tolongin Baim Ya Allah.. Amin.
For further discussion, please don’t hesitate to comment below! 💚