Dalam hal yang dimengerti oleh programmer OOP (tanpa latar belakang pemrograman fungsional), apa itu monad?
Masalah apa yang dipecahkan dan tempat apa yang paling umum digunakan?
EDIT:
Untuk memperjelas jenis pemahaman yang saya cari, katakanlah Anda mengubah aplikasi FP yang memiliki monads menjadi aplikasi OOP. Apa yang akan Anda lakukan untuk memindahkan tanggung jawab monad ke aplikasi OOP?
Jawaban:
UPDATE: Pertanyaan ini adalah subjek dari seri blog yang sangat panjang, yang dapat Anda baca di Monads - terima kasih atas pertanyaannya!
Monad adalah "penguat" jenis yang mematuhi aturan tertentu dan yang memiliki operasi tertentu disediakan .
Pertama, apa yang dimaksud dengan "penguat tipe"? Maksud saya beberapa sistem yang memungkinkan Anda mengambil jenis dan mengubahnya menjadi jenis yang lebih khusus. Sebagai contoh, dalam C # pertimbangkan
Nullable<T>
. Ini adalah penguat jenis. Itu memungkinkan Anda mengambil suatu tipe, mengatakanint
, dan menambahkan kemampuan baru ke tipe itu, yaitu, bahwa sekarang ia bisa menjadi nol ketika sebelumnya tidak bisa.Sebagai contoh kedua, pertimbangkan
IEnumerable<T>
. Ini adalah penguat jenis. Ini memungkinkan Anda mengambil suatu jenis, mengatakan,,string
dan menambahkan kemampuan baru ke jenis itu, yaitu, bahwa Anda sekarang dapat membuat urutan string dari sejumlah string tunggal.Apa "aturan tertentu"? Secara singkat, bahwa ada cara yang masuk akal untuk fungsi pada tipe yang mendasarinya untuk bekerja pada tipe yang diperkuat sehingga mereka mengikuti aturan normal komposisi fungsional. Misalnya, jika Anda memiliki fungsi pada bilangan bulat, katakanlah
maka fungsi yang sesuai pada
Nullable<int>
dapat membuat semua operator dan panggilan di sana bekerja bersama "dengan cara yang sama" yang mereka lakukan sebelumnya.(Itu sangat kabur dan tidak tepat; Anda meminta penjelasan yang tidak berasumsi tentang pengetahuan komposisi fungsional.)
Apa itu "operasi"?
Ada operasi "unit" (membingungkan kadang-kadang disebut operasi "kembali") yang mengambil nilai dari tipe polos dan menciptakan nilai monadik yang setara. Ini, pada dasarnya, menyediakan cara untuk mengambil nilai dari tipe yang tidak teramplifikasi dan mengubahnya menjadi nilai dari tipe yang diamplifikasi. Ini dapat diimplementasikan sebagai konstruktor dalam bahasa OO.
Ada operasi "bind" yang mengambil nilai monadik dan fungsi yang dapat mengubah nilai, dan mengembalikan nilai monadik baru. Bind adalah operasi utama yang mendefinisikan semantik monad. Ini memungkinkan kita mengubah operasi pada tipe yang tidak teramplifikasi menjadi operasi pada tipe yang diamplifikasi, yang mematuhi aturan komposisi fungsional yang disebutkan sebelumnya.
Sering ada cara untuk mendapatkan kembali tipe yang tidak teramplifikasi dari tipe yang diamplifikasi. Tegasnya operasi ini tidak wajib memiliki monad. (Meskipun perlu jika Anda ingin memiliki comonad . Kami tidak akan mempertimbangkan yang lebih lanjut dalam artikel ini.)
Sekali lagi, ambil
Nullable<T>
sebagai contoh. Anda dapat mengubah sebuahint
menjadiNullable<int>
dengan konstruktor. Kompiler C # menangani "lifting" yang paling dapat dibatalkan untuk Anda, tetapi jika tidak, transformasi pengangkatannya mudah: sebuah operasi, katakanlah,ditransformasikan menjadi
Dan mengubah
Nullable<int>
kembali menjadiint
selesai denganValue
properti.Ini adalah transformasi fungsi yang merupakan bit kunci. Perhatikan bagaimana semantik sebenarnya dari operasi yang dapat dibatalkan - bahwa operasi pada
null
propagasinull
- ditangkap dalam transformasi. Kita bisa menggeneralisasi ini.Misalkan Anda memiliki fungsi dari
int
hinggaint
, seperti aslinya kamiM
. Anda dapat dengan mudah menjadikannya sebagai fungsi yang mengambilint
dan mengembalikan aNullable<int>
karena Anda bisa menjalankan hasilnya melalui konstruktor yang dapat dibatalkan. Sekarang anggaplah Anda memiliki metode tingkat tinggi ini:Lihat apa yang bisa Anda lakukan dengan itu? Setiap metode yang mengambil
int
dan mengembalikan sebuahint
, atau mengambilint
dan mengembalikanNullable<int>
sekarang dapat memiliki semantik nullable diterapkan untuk itu .Selanjutnya: misalkan Anda memiliki dua metode
dan Anda ingin membuatnya:
Artinya,
Z
adalah komposisiX
danY
. Tetapi Anda tidak dapat melakukannya karenaX
membutuhkanint
, danY
mengembalikan aNullable<int>
. Tetapi karena Anda memiliki operasi "bind", Anda dapat membuatnya bekerja:Operasi mengikat pada monad adalah apa yang membuat komposisi fungsi pada jenis yang diperkuat bekerja. "Aturan" yang saya berikan di atas adalah bahwa monad mempertahankan aturan komposisi fungsi normal; yang menyusun dengan fungsi identitas menghasilkan fungsi asli, komposisi itu asosiatif, dan sebagainya.
Dalam C #, "Bind" disebut "SelectMany". Lihatlah cara kerjanya pada urutan monad. Kita perlu memiliki dua hal: mengubah nilai menjadi urutan dan mengikat operasi pada urutan. Sebagai bonus, kami juga memiliki "mengubah urutan kembali menjadi nilai". Operasi tersebut adalah:
Aturan monad yang dapat dibatalkan adalah "untuk menggabungkan dua fungsi yang menghasilkan nullables bersama, periksa untuk melihat apakah bagian dalam menghasilkan nol; jika itu, menghasilkan nol, jika tidak, maka panggil bagian luar dengan hasilnya". Itu semantik yang diinginkan dari nullable.
Aturan urutan monad adalah "untuk menggabungkan dua fungsi yang menghasilkan urutan bersama, menerapkan fungsi luar untuk setiap elemen yang dihasilkan oleh fungsi bagian dalam, dan kemudian menggabungkan semua urutan yang dihasilkan bersama-sama". Semantik dasar dari monad ditangkap dalam
Bind
/SelectMany
metode; ini adalah metode yang memberitahu Anda apa yang monad benar-benar berarti .Kita bisa melakukan yang lebih baik lagi. Misalkan Anda memiliki urutan int, dan metode yang mengambil int dan menghasilkan urutan string. Kita dapat menggeneralisasi operasi pengikatan untuk memungkinkan komposisi fungsi yang mengambil dan mengembalikan berbagai jenis yang diamplifikasi, selama input dari satu cocok dengan output dari yang lain:
Jadi sekarang kita dapat mengatakan "perkuat kumpulan bilangan bulat individual ini menjadi urutan bilangan bulat. Ubah bilangan bulat khusus ini menjadi sekelompok string, diamplifikasi ke urutan string. Sekarang satukan kedua operasi bersama: perkuat kumpulan integer ini ke dalam gabungan dari semua urutan string. " Monads memungkinkan Anda membuat amplifikasi.
Itu agak seperti bertanya "masalah apa yang dipecahkan oleh pola singleton?", Tetapi saya akan mencobanya.
Monads biasanya digunakan untuk menyelesaikan masalah seperti:
C # menggunakan monads dalam desainnya. Seperti yang telah disebutkan, pola nullable sangat mirip dengan "mungkin monad". LINQ seluruhnya dibangun dari monad; yang
SelectMany
metode adalah apa cara kerja semantik komposisi operasi. (Erik Meijer gemar menunjukkan bahwa setiap fungsi LINQ sebenarnya dapat diimplementasikan olehSelectMany
; segala sesuatu yang lain hanya kenyamanan.)Sebagian besar bahasa OOP tidak memiliki sistem tipe yang cukup kaya untuk mewakili pola monad itu sendiri secara langsung; Anda memerlukan sistem tipe yang mendukung tipe yang tipe lebih tinggi daripada tipe generik. Jadi saya tidak akan mencoba melakukan itu. Sebaliknya, saya akan menerapkan tipe generik yang mewakili masing-masing monad, dan menerapkan metode yang mewakili tiga operasi yang Anda butuhkan: mengubah nilai menjadi nilai yang diperkuat, (mungkin) mengubah nilai yang diperkuat menjadi nilai, dan mengubah fungsi pada nilai yang tidak teramplifikasi menjadi nilai sebuah fungsi pada nilai yang diamplifikasi.
Tempat yang baik untuk memulai adalah bagaimana kami mengimplementasikan LINQ di C #. Pelajari
SelectMany
metodenya; itu adalah kunci untuk memahami cara kerja urutan monad di C #. Ini adalah metode yang sangat sederhana, tetapi sangat kuat!Disarankan, bacaan lebih lanjut:
sumber
Mengapa kita membutuhkan monad?
Lalu, kita punya masalah besar pertama. Ini adalah program:
f(x) = 2 * x
g(x,y) = x / y
Bagaimana kita bisa mengatakan apa yang harus dieksekusi terlebih dahulu ? Bagaimana kita bisa membentuk urutan fungsi yang diurutkan (yaitu program ) menggunakan tidak lebih dari fungsi?
Solusi: menyusun fungsi . Jika Anda ingin pertama
g
dan kemudianf
, cukup tulisf(g(x,y))
. OKE, tapi ...Lebih banyak masalah: beberapa fungsi mungkin gagal (yaitu
g(2,0)
, bagi dengan 0). Kami tidak memiliki "pengecualian" di FP . Bagaimana kita menyelesaikannya?Solusi: Mari kita izinkan fungsi mengembalikan dua jenis hal : alih-alih memiliki
g : Real,Real -> Real
(fungsi dari dua real menjadi nyata), mari kita izinkang : Real,Real -> Real | Nothing
(fungsi dari dua real menjadi (nyata atau tidak sama sekali)).Tetapi fungsi seharusnya (lebih sederhana) mengembalikan hanya satu hal .
Solusi: mari kita buat tipe data baru yang akan dikembalikan, " tipe tinju " yang melingkupi mungkin nyata atau tidak menjadi apa-apa. Karena itu, kita dapat memiliki
g : Real,Real -> Maybe Real
. OKE, tapi ...Apa yang terjadi sekarang
f(g(x,y))
?f
belum siap untuk mengkonsumsiMaybe Real
. Dan, kami tidak ingin mengubah setiap fungsi yang kami dapat hubungkan dengang
untuk mengkonsumsi aMaybe Real
.Solusi: mari kita memiliki fungsi khusus untuk "menghubungkan" / "menulis" / "tautan" fungsi . Dengan begitu, kita dapat, di belakang layar, mengadaptasi output dari satu fungsi untuk memberi makan fungsi berikut.
Dalam kasus kami:
g >>= f
(hubungkan / tulisg
kef
). Kami ingin>>=
mendapatkang
hasil, memeriksanya dan, kalau-kalauNothing
tidak meneleponf
dan kembaliNothing
; atau sebaliknya, ekstrak kotakReal
dan beri makanf
dengan itu. (Algoritma ini hanya implementasi>>=
untukMaybe
tipe).Banyak masalah lain muncul yang dapat dipecahkan dengan menggunakan pola yang sama: 1. Gunakan "kotak" untuk mengkodifikasi / menyimpan makna / nilai yang berbeda, dan memiliki fungsi seperti
g
itu mengembalikan "nilai kotak" itu. 2. Miliki komposer / penghubungg >>= f
untuk membantu menghubungkang
keluaran kef
input, jadi kami tidak perlu mengubahf
sama sekali.Masalah luar biasa yang dapat dipecahkan dengan menggunakan teknik ini adalah:
memiliki keadaan global yang setiap fungsi dalam urutan fungsi ("program") dapat membagikan: solusi
StateMonad
.Kami tidak suka "fungsi tidak murni": fungsi yang menghasilkan output berbeda untuk input yang sama . Oleh karena itu, mari tandai fungsi-fungsi tersebut, membuatnya untuk mengembalikan nilai yang ditandai / kotak:
IO
monad.Total kebahagiaan !!!!
sumber
State
danIO
, tanpa satu pun dari mereka serta makna yang tepat dari "monad" yang diberikanSaya akan mengatakan analogi OO terdekat dengan monad adalah " pola perintah ".
Dalam pola perintah Anda membungkus pernyataan atau ekspresi biasa dalam objek perintah . Objek perintah mengekspos metode eksekusi yang mengeksekusi pernyataan terbungkus. Jadi pernyataan diubah menjadi objek kelas satu yang dapat diedarkan dan dieksekusi sesuka hati. Perintah dapat dikomposisikan sehingga Anda dapat membuat objek-program dengan merantai dan membuat objek-perintah.
Perintah dijalankan oleh objek terpisah, penyerang . Manfaat menggunakan pola perintah (daripada hanya menjalankan serangkaian pernyataan biasa) adalah bahwa penjajah yang berbeda dapat menerapkan logika yang berbeda dengan bagaimana perintah harus dieksekusi.
Pola perintah dapat digunakan untuk menambah (atau menghapus) fitur bahasa yang tidak didukung oleh bahasa host. Misalnya, dalam bahasa OO hipotetis tanpa pengecualian, Anda bisa menambahkan pengecualian semantik dengan memaparkan metode "coba" dan "buang" ke perintah. Ketika sebuah perintah memanggil lemparan, penyerang mundur melalui daftar (atau pohon) perintah sampai panggilan "coba" terakhir. Sebaliknya, Anda bisa menghapus pengecualian semantik dari bahasa (jika Anda yakin pengecualian itu buruk ) dengan menangkap semua pengecualian yang dilemparkan oleh masing-masing perintah individu, dan mengubahnya menjadi kode kesalahan yang kemudian diteruskan ke perintah berikutnya.
Bahkan semantik eksekusi yang lebih mewah seperti transaksi, eksekusi non-deterministik atau kelanjutan dapat diimplementasikan seperti ini dalam bahasa yang tidak mendukungnya secara asli. Ini adalah pola yang sangat kuat jika Anda memikirkannya.
Sekarang dalam kenyataannya pola perintah tidak digunakan sebagai fitur bahasa umum seperti ini. Overhead mengubah setiap pernyataan menjadi kelas yang terpisah akan menyebabkan jumlah kode boilerplate yang tak tertahankan. Tetapi pada prinsipnya itu dapat digunakan untuk memecahkan masalah yang sama seperti monad digunakan untuk menyelesaikan dalam fp.
sumber
Dalam hal pemrograman OO, monad adalah antarmuka (atau lebih mungkin mixin), diparameterisasi berdasarkan tipe, dengan dua metode,
return
danbind
yang menggambarkan:Masalah yang dipecahkannya adalah jenis masalah yang sama dengan yang Anda harapkan dari antarmuka apa pun, yaitu, "Saya memiliki banyak kelas berbeda yang melakukan hal yang berbeda, tetapi tampaknya melakukan hal-hal yang berbeda dengan cara yang memiliki kesamaan mendasar. dapatkah saya menggambarkan kesamaan di antara mereka, bahkan jika kelas itu sendiri tidak benar-benar subtipe dari sesuatu yang lebih dekat daripada kelas 'Objek' itu sendiri? "
Lebih khusus lagi,
Monad
"antarmuka" mirip denganIEnumerator
atauIIterator
dalam hal itu mengambil jenis yang itu sendiri mengambil jenis. "Titik" utamaMonad
adalah mampu menghubungkan operasi berdasarkan tipe interior, bahkan ke titik memiliki "tipe internal" baru, sambil menjaga - atau bahkan meningkatkan - struktur informasi kelas utama.sumber
return
tidak akan benar-benar menjadi metode pada monad, karena itu tidak mengambil contoh monad sebagai argumen. (yaitu: tidak ada ini / diri)Anda memiliki presentasi baru-baru ini " Monadologie - bantuan profesional tentang kecemasan tipe " oleh Christopher League (12 Juli 2010), yang cukup menarik pada topik kelanjutan dan monad.
Video yang disertai presentasi (slideshare) ini sebenarnya tersedia di vimeo .
Bagian Monad dimulai sekitar 37 menit dalam, pada video satu jam ini, dan dimulai dengan slide 42 dari 58 presentasi slide.
Ini disajikan sebagai "pola desain terkemuka untuk pemrograman fungsional", tetapi bahasa yang digunakan dalam contoh adalah Scala, yang merupakan OOP dan fungsional.
Anda dapat membaca lebih lanjut tentang Monad di Scala di posting blog " Monads - Cara lain untuk menghitung abstrak di Scala ", dari Debasish Ghosh (27 Maret 2008).
Jadi misalnya (dalam Scala):
Option
adalah monadList
adalah MonadMonad adalah masalah besar di Scala karena sintaksis yang mudah dibangun untuk memanfaatkan struktur Monad:
for
pemahaman dalam Scala :diterjemahkan oleh kompiler ke:
Kunci abstraksi adalah
flatMap
, yang mengikat perhitungan melalui rantai.Setiap doa
flatMap
mengembalikan tipe struktur data yang sama (tapi nilainya berbeda), yang berfungsi sebagai input ke perintah berikutnya dalam rantai.Dalam cuplikan di atas, flatMap mengambil sebagai input penutupan
(SomeType) => List[AnotherType]
dan mengembalikan aList[AnotherType]
. Poin penting yang perlu diperhatikan adalah bahwa semua flatMaps mengambil tipe penutupan yang sama dengan input dan mengembalikan tipe yang sama dengan output.Inilah yang "mengikat" utas perhitungan - setiap item dari urutan dalam untuk pemahaman harus menghormati batasan jenis yang sama ini.
Jika Anda mengambil dua operasi (yang mungkin gagal) dan meneruskan hasilnya ke yang ketiga, seperti:
tetapi tanpa memanfaatkan Monad, Anda mendapatkan kode OOP yang berbelit-belit seperti:
sedangkan dengan Monad, Anda dapat bekerja dengan tipe aktual (
Venue
,User
) seperti semua operasi, dan merahasiakan opsi verifikasi Opsi, semuanya karena flatmap dari sintaks untuk:Bagian hasil hanya akan dieksekusi jika ketiga fungsi memiliki
Some[X]
; semuaNone
akan langsung dikembalikan keconfirm
.Begitu:
Omong-omong, Monad bukan hanya model perhitungan yang digunakan dalam FP:
sumber
Untuk menghormati pembaca cepat, saya mulai dengan definisi yang tepat terlebih dahulu, lanjutkan dengan penjelasan yang lebih cepat "bahasa Inggris polos", dan kemudian pindah ke contoh.
Berikut adalah definisi ringkas dan tepat yang sedikit ditulis ulang:
Jadi dengan kata sederhana, monad adalah aturan untuk berpindah dari tipe apa pun
X
ke tipe lainT(X)
, dan aturan untuk berpindah dari dua fungsif:X->T(Y)
dang:Y->T(Z)
(yang ingin Anda buat tetapi tidak bisa) ke fungsi baruh:X->T(Z)
. Namun, yang bukan komposisi dalam arti matematika yang ketat. Kami pada dasarnya adalah "menekuk" komposisi fungsi atau mendefinisikan kembali bagaimana fungsi disusun.Plus, kita membutuhkan aturan monad untuk menyusun untuk memenuhi aksioma matematika "jelas":
f
dengang
dan kemudian denganh
(dari luar) harus sama dengan menulisg
denganh
dan kemudian denganf
(dari dalam).f
dengan fungsi identitas di kedua sisi harus menghasilkanf
.Sekali lagi, dengan kata-kata sederhana, kita tidak bisa menjadi gila mendefinisikan ulang komposisi fungsi kita seperti yang kita inginkan:
f(g(h(k(x)))
, misalnya , dan tidak perlu khawatir menentukan urutan yang menyusun pasangan fungsi. Karena aturan monad hanya mengatur cara membuat sepasang fungsi , tanpa aksioma itu, kita perlu mengetahui pasangan mana yang pertama kali disusun dan seterusnya. (Catatan yang berbeda dari properti komutatif yangf
dikomposisig
sama dengan yangg
dikomposisikanf
, yang tidak diperlukan).Jadi sekali lagi secara singkat: Monad adalah aturan ekstensi tipe dan fungsi penulisan yang memuaskan dua aksioma - asosiasi dan properti unital.
Dalam istilah praktis, Anda ingin monad diimplementasikan untuk Anda oleh bahasa, kompiler atau kerangka kerja yang akan mengatur fungsi penulisan untuk Anda. Jadi Anda bisa fokus menulis logika fungsi Anda daripada mengkhawatirkan bagaimana eksekusi mereka diimplementasikan.
Singkatnya, singkatnya.
Menjadi ahli matematika profesional, saya lebih suka menghindari menyebut
h
"komposisi" darif
dang
. Karena secara matematis, tidak. Menyebutnya "komposisi" salah mengira ituh
adalah komposisi matematika yang benar, yang tidak. Bahkan tidak ditentukan secara unik olehf
dang
. Alih-alih, itu adalah hasil dari "aturan komposisi" fungsi baru monad kami. Yang bisa sangat berbeda dari komposisi matematika yang sebenarnya bahkan jika yang terakhir ada!Agar kurang kering, izinkan saya mencoba mengilustrasikannya dengan contoh bahwa saya membuat anotasi dengan bagian-bagian kecil, sehingga Anda dapat langsung beralih ke intinya.
Melontarkan pengecualian sebagai contoh Monad
Misalkan kita ingin membuat dua fungsi:
Tetapi
f(0)
tidak didefinisikan, jadi pengecualiane
dilemparkan. Lalu bagaimana Anda bisa mendefinisikan nilai komposisig(f(0))
? Lempar pengecualian lagi, tentu saja! Mungkin samae
. Mungkin pengecualian baru yang diperbaruie1
.Apa tepatnya yang terjadi di sini? Pertama, kita membutuhkan nilai pengecualian baru (berbeda atau sama). Anda dapat memanggil mereka
nothing
ataunull
atau apa pun tetapi esensinya tetap sama - mereka harus berupa nilai baru, misalnya tidak bolehnumber
dalam contoh kita di sini. Saya lebih suka untuk tidak memanggil merekanull
untuk menghindari kebingungan dengan bagaimananull
dapat diimplementasikan dalam bahasa tertentu. Saya juga lebih suka menghindarinothing
karena sering dikaitkan dengannull
, yang, pada prinsipnya, adalah apa yangnull
harus dilakukan, prinsip itu sering ditekuk karena alasan praktis apa pun.Apa sebenarnya pengecualian itu?
Ini adalah masalah sepele untuk setiap programmer berpengalaman tetapi saya ingin memberikan beberapa kata hanya untuk memadamkan worm kebingungan:
Pengecualian adalah objek yang merangkum informasi tentang bagaimana hasil eksekusi yang tidak valid terjadi.
Ini dapat berkisar dari membuang semua detail dan mengembalikan nilai global tunggal (seperti
NaN
ataunull
) atau membuat daftar log panjang atau apa yang sebenarnya terjadi, mengirimkannya ke database dan mereplikasi seluruh lapisan penyimpanan data terdistribusi;)Perbedaan penting antara dua contoh pengecualian ekstrim ini adalah bahwa dalam kasus pertama tidak ada efek samping . Yang kedua ada. Yang membawa kita pada pertanyaan (ribuan dolar):
Apakah pengecualian diperbolehkan dalam fungsi murni?
Jawaban yang lebih singkat : Ya, tetapi hanya ketika mereka tidak mengarah ke efek samping.
Jawaban yang lebih panjang. Agar murni, output fungsi Anda harus ditentukan secara unik oleh inputnya. Jadi kami mengubah fungsi kami
f
dengan mengirimkan0
ke nilai abstrak barue
yang kami sebut pengecualian. Kami memastikan bahwa nilaie
tidak mengandung informasi luar yang tidak ditentukan secara unik oleh input kami, yaitux
. Jadi di sini adalah contoh pengecualian tanpa efek samping:Dan inilah efek sampingnya:
Sebenarnya, itu hanya memiliki efek samping jika pesan itu dapat berubah di masa depan. Tetapi jika dijamin tidak akan pernah berubah, nilai itu menjadi dapat diprediksi secara unik, sehingga tidak ada efek samping.
Untuk membuatnya lebih konyol. Suatu fungsi yang
42
pernah kembali jelas murni. Tetapi jika seseorang gila memutuskan untuk membuat42
variabel yang nilainya mungkin berubah, fungsi yang sama berhenti menjadi murni di bawah kondisi baru.Perhatikan bahwa saya menggunakan objek notasi literal untuk kesederhanaan untuk menunjukkan esensi. Sayangnya hal-hal kacau dalam bahasa seperti JavaScript, di mana
error
bukan jenis yang berperilaku seperti yang kita inginkan di sini sehubungan dengan komposisi fungsi, sedangkan jenis yang sebenarnya sukanull
atauNaN
tidak berperilaku seperti ini melainkan melalui beberapa buatan dan tidak selalu intuitif ketik konversi.Ketik ekstensi
Karena kami ingin memvariasikan pesan di dalam pengecualian kami, kami benar-benar mendeklarasikan tipe baru
E
untuk keseluruhan objek pengecualian dan kemudian Itulah yangmaybe number
dilakukannya, selain dari namanya yang membingungkan, yang bisa berupa tipenumber
atau tipe pengecualian baruE
, sehingga benar-benar serikatnumber | E
darinumber
danE
. Secara khusus, itu tergantung pada bagaimana kita ingin membangunE
, yang tidak disarankan atau tercermin dalam namanyamaybe number
.Apa itu komposisi fungsional?
Ini adalah operasi matematika mengambil fungsi
f: X -> Y
dang: Y -> Z
dan membangun komposisi mereka sebagai fungsi yangh: X -> Z
memuaskanh(x) = g(f(x))
. Masalah dengan definisi ini terjadi ketika hasilnyaf(x)
tidak diizinkan sebagai argumeng
.Dalam matematika, fungsi-fungsi itu tidak dapat disusun tanpa kerja ekstra. Solusi matematis ketat untuk contoh di atas
f
dang
adalah untuk menghapus0
dari set definisif
. Dengan seperangkat definisi baru (tipe baru yang lebih ketatx
), dapatf
dikomposisikan dengang
.Namun, sangat tidak praktis dalam pemrograman untuk membatasi sekumpulan definisi
f
seperti itu. Sebagai gantinya, pengecualian dapat digunakan.Atau sebagai pendekatan lain, nilai-nilai buatan yang dibuat seperti
NaN
,undefined
,null
,Infinity
dll Jadi Anda mengevaluasi1/0
untukInfinity
dan1/-0
untuk-Infinity
. Dan kemudian paksa nilai baru kembali ke ekspresi Anda alih-alih melemparkan pengecualian. Mengarah ke hasil yang Anda mungkin atau mungkin tidak dapat diprediksi:Dan kami kembali ke nomor reguler yang siap untuk pindah;)
JavaScript memungkinkan kami untuk terus mengeksekusi ekspresi numerik dengan biaya berapa pun tanpa menimbulkan kesalahan seperti pada contoh di atas. Itu berarti, itu juga memungkinkan untuk menulis fungsi. Yang persis tentang apa itu monad - itu adalah aturan untuk menyusun fungsi yang memuaskan aksioma seperti yang didefinisikan pada awal jawaban ini.
Tetapi apakah aturan penyusunan fungsi, yang timbul dari implementasi JavaScript untuk menangani kesalahan numerik, monad?
Untuk menjawab pertanyaan ini, yang Anda butuhkan adalah memeriksa aksioma (dibiarkan sebagai latihan bukan bagian dari pertanyaan di sini;).
Bisakah melempar pengecualian digunakan untuk membangun monad?
Memang, monad yang lebih bermanfaat sebagai gantinya adalah aturan yang menetapkan bahwa jika
f
melempar pengecualian untuk beberapa orangx
, demikian juga komposisinya dengan apapung
. Plus membuat pengecualianE
secara global unik dengan hanya satu nilai yang mungkin ( objek terminal dalam teori kategori). Sekarang kedua aksioma dapat langsung diperiksa dan kita mendapatkan monad yang sangat berguna. Dan hasilnya adalah apa yang dikenal sebagai monad mungkin .sumber
Monad adalah tipe data yang merangkum nilai, dan yang pada dasarnya, dua operasi dapat diterapkan:
return x
menciptakan nilai tipe monad yang merangkumx
m >>= f
(membacanya sebagai "operator mengikat") menerapkan fungsif
ke nilai di monadm
Itulah monad itu. Ada beberapa teknis lagi , tetapi pada dasarnya kedua operasi tersebut mendefinisikan sebuah monad. Pertanyaan sebenarnya adalah, "Apa yang dilakukan monad ?", Dan itu tergantung pada daftar - monad adalah monad, Maybes adalah monad, operasi IO adalah monad. Semua itu artinya ketika kita mengatakan hal-hal itu adalah monad adalah bahwa mereka memiliki antarmuka monad
return
dan>>=
.sumber
bind
fungsi yang harus didefinisikan untuk setiap tipe monadik, bukan? Itu akan menjadi alasan yang baik untuk tidak membingungkan ikatan dengan komposisi, karena ada definisi tunggal untuk komposisi, sementara tidak ada hanya definisi tunggal untuk fungsi mengikat, ada satu per tipe monadik, jika saya mengerti dengan benar.Dari wikipedia :
Saya percaya ini menjelaskan dengan sangat baik.
sumber
Saya akan mencoba membuat definisi terpendek yang dapat saya kelola dengan menggunakan istilah OOP:
Kelas generik
CMonadic<T>
adalah monad jika mendefinisikan setidaknya metode berikut:dan jika undang-undang berikut berlaku untuk semua jenis T dan nilainya yang mungkin t
identitas kiri:
identitas yang benar
asosiatif:
Contoh :
Daftar monad mungkin memiliki:
Dan flatMap pada daftar [1,2,3] dapat berfungsi seperti:
Iterables dan Observable juga dapat dibuat monadik, serta Janji dan Tugas.
Komentar :
Monad tidak serumit itu. The
flatMap
Fungsi adalah banyak seperti yang lebih umum ditemuimap
. Ini menerima argumen fungsi (juga dikenal sebagai delegasi), yang dapat dipanggil (segera atau lambat, nol atau lebih kali) dengan nilai yang berasal dari kelas generik. Ia mengharapkan bahwa fungsi yang lewat juga membungkus nilai kembalinya dalam jenis generik yang sama. Untuk membantu itu, ia menyediakancreate
, sebuah konstruktor yang dapat membuat turunan dari kelas generik dari suatu nilai. Hasil kembalinya flatMap juga merupakan kelas generik dari jenis yang sama, seringkali mengemas nilai yang sama yang terkandung dalam hasil kembalinya satu atau lebih aplikasi flatMap ke nilai yang sebelumnya terkandung. Ini memungkinkan Anda untuk membuat rantai flatMap sebanyak yang Anda inginkan:Kebetulan kelas generik semacam ini berguna sebagai model dasar untuk banyak hal. Ini (bersama dengan teori kategori jargonisme) adalah alasan mengapa Monads tampak begitu sulit untuk dipahami atau dijelaskan. Mereka adalah hal yang sangat abstrak dan hanya menjadi jelas berguna setelah mereka terspesialisasi.
Misalnya, Anda bisa memodelkan pengecualian menggunakan wadah monadik. Setiap kontainer akan berisi hasil operasi atau kesalahan yang telah terjadi. Fungsi berikutnya (mendelegasikan) dalam rantai panggilan balik flatMap hanya akan dipanggil jika yang sebelumnya mengemas nilai dalam wadah. Kalau tidak, jika kesalahan telah dikemas, kesalahan akan terus menyebar melalui wadah berantai sampai ditemukan wadah yang memiliki fungsi penangan kesalahan yang dilampirkan melalui metode yang disebut
.orElse()
(metode seperti itu akan menjadi ekstensi yang diizinkan)Catatan : Bahasa fungsional memungkinkan Anda untuk menulis fungsi yang dapat beroperasi pada semua jenis kelas generik monadik. Agar ini berfungsi, orang harus menulis antarmuka generik untuk monad. Saya tidak tahu apakah mungkin untuk menulis antarmuka seperti itu di C #, tetapi sejauh yang saya tahu tidak:
sumber
Apakah monad memiliki interpretasi "alami" dalam OO tergantung pada monad. Dalam bahasa seperti Java, Anda dapat menerjemahkan monad mungkin ke bahasa memeriksa pointer nol, sehingga perhitungan yang gagal (yaitu, menghasilkan Tidak ada di Haskell) memancarkan null pointer sebagai hasilnya. Anda dapat menerjemahkan negara monad ke dalam bahasa yang dihasilkan dengan membuat variabel yang dapat berubah dan metode untuk mengubah keadaannya.
Monad adalah monoid dalam kategori endofunctors.
Informasi yang dikumpulkan oleh kalimat itu sangat dalam. Dan Anda bekerja di monad dengan bahasa imperatif apa pun. Monad adalah bahasa khusus domain "berurutan". Ini memenuhi sifat menarik tertentu, yang diambil bersama-sama membuat monad model matematika "pemrograman imperatif". Haskell memudahkan untuk mendefinisikan bahasa imperatif kecil (atau besar), yang dapat digabungkan dalam berbagai cara.
Sebagai seorang programmer OO, Anda menggunakan hierarki kelas bahasa Anda untuk mengatur jenis fungsi atau prosedur yang dapat dipanggil dalam konteks, apa yang Anda sebut objek. Monad juga merupakan abstraksi ide ini, sejauh monad yang berbeda dapat digabungkan dengan cara yang sewenang-wenang, secara efektif "mengimpor" semua metode sub-monad ke dalam ruang lingkup.
Secara arsitektural, seseorang kemudian menggunakan tipe tanda tangan untuk secara eksplisit menyatakan konteks mana yang dapat digunakan untuk menghitung nilai.
Seseorang dapat menggunakan trafo monad untuk tujuan ini, dan ada koleksi berkualitas tinggi dari semua monad "standar":
dengan transformer monad yang sesuai dan kelas tipe. Kelas tipe memungkinkan pendekatan komplementer untuk menggabungkan monad dengan menyatukan antarmuka mereka, sehingga monad konkret dapat mengimplementasikan antarmuka standar untuk "jenis" monad. Sebagai contoh, module Control.Monad.State berisi kelas MonadState sm, dan (State s) adalah turunan dari form
Ceritanya yang panjang adalah bahwa monad adalah fungsi yang melampirkan "konteks" ke suatu nilai, yang memiliki cara untuk menyuntikkan nilai ke dalam monad, dan yang memiliki cara untuk mengevaluasi nilai sehubungan dengan konteks yang melekat padanya, setidaknya secara terbatas.
Begitu:
adalah fungsi yang menyuntikkan nilai tipe a ke "aksi" monad tipe m a.
adalah fungsi yang mengambil tindakan monad, mengevaluasi hasilnya, dan menerapkan fungsi pada hasilnya. Yang rapi tentang (>> =) adalah hasilnya di monad yang sama. Dengan kata lain, dalam m >> = f, (>> =) menarik hasilnya dari m, dan mengikatnya ke f, sehingga hasilnya ada di monad. (Atau, kita dapat mengatakan bahwa (>> =) menarik f ke m dan menerapkannya pada hasilnya.) Sebagai konsekuensinya, jika kita memiliki f :: a -> mb, dan g :: b -> mc, kita dapat "urutan" tindakan:
Atau, menggunakan "do notation"
Jenis untuk (>>) mungkin menerangi. ini
Ini sesuai dengan operator (;) dalam bahasa prosedural seperti C. Memungkinkan notasi seperti:
Dalam logika matematika dan filosofis, kita memiliki bingkai dan model, yang "secara alami" dimodelkan dengan monadisme. Interpretasi adalah fungsi yang melihat ke dalam domain model dan menghitung nilai kebenaran (atau generalisasi) dari proposisi (atau formula, di bawah generalisasi). Dalam logika modal untuk keperluan, kita dapat mengatakan bahwa proposisi diperlukan jika itu benar dalam "setiap dunia yang mungkin" - jika itu benar sehubungan dengan setiap domain yang dapat diterima. Ini berarti bahwa model dalam bahasa untuk suatu proposisi dapat diverifikasi sebagai model yang domainnya terdiri dari kumpulan model yang berbeda (satu yang sesuai dengan setiap dunia yang mungkin). Setiap monad memiliki metode bernama "join" yang meratakan lapisan, yang menyiratkan bahwa setiap tindakan monad yang hasilnya adalah tindakan monad dapat tertanam dalam monad.
Lebih penting lagi, itu berarti bahwa monad ditutup di bawah operasi "lapisan susun". Inilah cara kerja transformer monad: mereka menggabungkan monad dengan menyediakan metode "bergabung-seperti" untuk tipe-tipe seperti
sehingga kita bisa mengubah aksi dalam (MaybeT m) menjadi aksi dalam m, secara efektif menghancurkan lapisan. Dalam hal ini, runMaybeT :: MaybeT ma -> m (Mungkin a) adalah metode join-like kami. (MaybeT m) adalah monad, dan MaybeT :: m (Maybe a) -> MaybeT ma secara efektif merupakan konstruktor untuk tipe baru aksi monad di m.
Monad gratis untuk functor adalah monad yang dihasilkan oleh penumpukan f, dengan implikasi bahwa setiap urutan konstruktor untuk f adalah elemen dari monad bebas (atau, lebih tepatnya, sesuatu dengan bentuk yang sama dengan pohon urutan konstruktor untuk f). Monad gratis adalah teknik yang berguna untuk membangun monad fleksibel dengan jumlah pelat ketel yang minimal. Dalam program Haskell, saya dapat menggunakan monad gratis untuk mendefinisikan monad sederhana untuk "pemrograman sistem tingkat tinggi" untuk membantu menjaga keamanan tipe (Saya hanya menggunakan tipe dan deklarasi mereka. Implementasinya sangat mudah dengan penggunaan kombinator):
Monadisme adalah arsitektur yang mendasari apa yang Anda sebut pola "interpreter" atau "command", disarikan ke bentuk yang paling jelas, karena setiap perhitungan monadik harus "dijalankan", setidaknya sepele. (Sistem runtime menjalankan monad IO untuk kita, dan merupakan titik masuk ke program Haskell. IO "menggerakkan" sisa perhitungan, dengan menjalankan tindakan IO secara berurutan).
Jenis untuk bergabung juga di mana kita mendapatkan pernyataan bahwa monad adalah monoid dalam kategori endofunctors. Gabung biasanya lebih penting untuk tujuan teoritis, berdasarkan jenisnya. Tetapi memahami tipenya berarti memahami monad. Tipe join-like dan join monad transformer adalah komposisi endofunctor yang efektif, dalam arti komposisi fungsi. Untuk memasukkannya ke dalam bahasa pseudo seperti Haskell,
Foo :: m (ma) <-> (m. M) a
sumber
Monad adalah array fungsi
(PST: berbagai fungsi hanyalah perhitungan).
Sebenarnya, alih-alih array yang benar (satu fungsi dalam satu array sel) Anda memiliki fungsi-fungsi tersebut dirantai oleh fungsi lain >> =. >> = memungkinkan untuk mengadaptasi hasil dari fungsi i untuk memberi makan fungsi i + 1, melakukan perhitungan di antara mereka atau, bahkan, untuk tidak memanggil fungsi i +1.
Jenis yang digunakan di sini adalah "jenis dengan konteks". Ini adalah nilai dengan "tag". Fungsi yang dirantai harus mengambil "nilai telanjang" dan mengembalikan hasil yang ditandai. Salah satu tugas >> = adalah mengekstraksi nilai telanjang di luar konteksnya. Ada juga fungsi "kembali", yang mengambil nilai telanjang dan meletakkannya dengan tag.
Contoh dengan Maybe . Mari kita gunakan untuk menyimpan bilangan bulat sederhana tempat membuat perhitungan.
Hanya untuk menunjukkan bahwa monad adalah array fungsi dengan operasi pembantu, pertimbangkan yang setara dengan contoh di atas, hanya menggunakan array fungsi yang nyata
Dan itu akan digunakan seperti ini:
sumber
>>=
adalah operator\x -> x >>= k >>= l >>= m
array fungsi, demikian jugah . g . f
, yang tidak melibatkan monads sama sekali.Dalam istilah OO, monad adalah wadah yang lancar.
Persyaratan minimum adalah definisi
class <A> Something
yang mendukung konstruktorSomething(A a)
dan setidaknya satu metodeSomething<B> flatMap(Function<A, Something<B>>)
Dapat diperdebatkan, ini juga diperhitungkan jika kelas monad Anda memiliki metode apa pun dengan tanda tangan
Something<B> work()
yang mempertahankan aturan kelas - pembuat kompiler dalam flatMap pada waktu kompilasi.Mengapa monad bermanfaat? Karena itu adalah wadah yang memungkinkan operasi berantai yang melestarikan semantik. Misalnya,
Optional<?>
mempertahankan semantik isPresent untukOptional<String>
,Optional<Integer>
,Optional<MyClass>
, dllSebagai contoh kasar,
Catatan kita mulai dengan string dan diakhiri dengan integer. Cukup keren.
Dalam OO, mungkin perlu sedikit melambaikan tangan, tetapi metode apa pun pada Sesuatu yang mengembalikan subkelas Sesuatu lainnya memenuhi kriteria fungsi wadah yang mengembalikan wadah dari jenis aslinya.
Itulah cara Anda mempertahankan semantik - artinya makna dan operasi wadah tidak berubah, mereka hanya membungkus dan meningkatkan objek di dalam wadah.
sumber
Monads dalam penggunaan tipikal adalah fungsional yang setara dengan mekanisme penanganan pengecualian pemrograman prosedural.
Dalam bahasa prosedural modern, Anda menempatkan penangan pengecualian di sekitar urutan pernyataan, yang salah satu di antaranya bisa melempar pengecualian. Jika salah satu dari pernyataan melempar pengecualian, eksekusi normal dari urutan pernyataan berhenti dan transfer ke pengendali pengecualian.
Namun, bahasa pemrograman fungsional secara filosofis menghindari fitur penanganan pengecualian karena sifat "goto" seperti mereka. Perspektif pemrograman fungsional adalah bahwa fungsi tidak boleh memiliki "efek samping" seperti pengecualian yang mengganggu aliran program.
Pada kenyataannya, efek samping tidak dapat dikesampingkan di dunia nyata terutama karena I / O. Monads dalam pemrograman fungsional digunakan untuk menangani hal ini dengan mengambil satu set panggilan fungsi berantai (yang mana mana mungkin menghasilkan hasil yang tidak terduga) dan mengubah setiap hasil yang tidak terduga menjadi data yang dienkapsulasi yang masih dapat mengalir dengan aman melalui panggilan fungsi yang tersisa.
Aliran kontrol dipertahankan tetapi kejadian yang tidak terduga ini dikemas dan ditangani dengan aman.
sumber
Penjelasan Monads sederhana dengan studi kasus Marvel ada di sini .
Monads adalah abstraksi yang digunakan untuk mengurutkan fungsi-fungsi dependen yang efektif. Efektif di sini berarti mereka mengembalikan jenis dalam bentuk F [A] misalnya Opsi [A] di mana Opsi adalah F, yang disebut konstruktor tipe. Mari kita lihat ini dalam 2 langkah sederhana
Namun, jika fungsi mengembalikan jenis efek seperti Opsi [A] yaitu A => F [B] komposisi tidak berfungsi untuk pergi ke B kita perlu A => B tetapi kita memiliki A => F [B].
Kami membutuhkan operator khusus, "bind" yang tahu cara menggabungkan fungsi-fungsi ini yang mengembalikan F [A].
Fungsi "bind" didefinisikan untuk F tertentu .
Ada juga "return" , dari tipe A => F [A] untuk setiap A , yang didefinisikan untuk F tertentu juga. Untuk menjadi Monad, F harus memiliki dua fungsi yang ditentukan untuknya.
Dengan demikian kita dapat membangun fungsi efektif A => F [B] dari fungsi murni A => B ,
tetapi F yang diberikan juga dapat mendefinisikan fungsi khusus "bawaan" buram jenisnya sehingga pengguna tidak dapat menentukan sendiri (dalam bahasa murni ), seperti
sumber
Saya berbagi pemahaman saya tentang Monads, yang secara teori mungkin tidak sempurna. Monads adalah tentang propagasi konteks . Monad adalah, Anda mendefinisikan beberapa konteks untuk beberapa data (atau tipe data), dan kemudian menentukan bagaimana konteks itu akan dibawa dengan data di sepanjang pipa pemrosesan. Dan mendefinisikan propagasi konteks sebagian besar tentang mendefinisikan bagaimana menggabungkan beberapa konteks (dari tipe yang sama). Menggunakan Monads juga berarti memastikan bahwa konteks ini tidak secara tidak sengaja dihapus dari data. Di sisi lain, data tanpa konteks lainnya dapat dibawa ke konteks baru atau yang sudah ada. Maka konsep sederhana ini dapat digunakan untuk memastikan kompilasi ketepatan waktu suatu program.
sumber
Jika Anda pernah menggunakan Powershell, pola yang dijelaskan Eric seharusnya terdengar familier. Cmdlet Powershell adalah monad; komposisi fungsional diwakili oleh pipa .
Wawancara Jeffrey Snover dengan Erik Meijer lebih detail.
sumber
Lihat jawaban saya untuk "Apa itu monad?"
Itu dimulai dengan sebuah contoh yang memotivasi, bekerja melalui contoh itu, mengambil contoh dari sebuah monad, dan secara resmi mendefinisikan "monad".
Ini mengasumsikan tidak memiliki pengetahuan tentang pemrograman fungsional dan menggunakan pseudocode dengan
function(argument) := expression
sintaks dengan ekspresi sesederhana mungkin.Program C ++ ini merupakan implementasi dari pseudocode monad. (Untuk referensi:
M
adalah konstruktor tipe,feed
adalah operasi "bind", danwrap
merupakan operasi "return".)sumber
Dari sudut pandang praktis (meringkas apa yang telah dikatakan dalam banyak jawaban sebelumnya dan artikel terkait), bagi saya tampaknya salah satu "tujuan" (atau kegunaan) mendasar dari monad adalah untuk meningkatkan ketergantungan yang tersirat dalam doa metode rekursif. alias komposisi fungsi (yaitu ketika f1 memanggil f2 memanggil f3, f3 perlu dievaluasi sebelum f2 sebelum f1) untuk mewakili komposisi berurutan dengan cara alami, terutama dalam konteks model evaluasi malas (yaitu, komposisi sekuensial sebagai urutan polos , misalnya "f3 (); f2 (); f1 ();" dalam C - triknya sangat jelas jika Anda memikirkan kasus di mana f3, f2 dan f1 sebenarnya tidak mengembalikan apa pun [rantai mereka sebagai f1 (f2 (f3)) buatan, murni dimaksudkan untuk membuat urutan]).
Ini sangat relevan ketika efek samping terlibat, yaitu ketika beberapa keadaan diubah (jika f1, f2, f3 tidak memiliki efek samping, tidak masalah dalam urutan apa mereka dievaluasi; yang merupakan properti besar murni bahasa fungsional, untuk dapat memparalelkan perhitungan tersebut misalnya). Semakin murni fungsinya, semakin baik.
Saya pikir dari sudut pandang sempit itu, monad dapat dilihat sebagai gula sintaksis untuk bahasa yang mendukung evaluasi malas (yang mengevaluasi hal-hal hanya ketika benar-benar diperlukan, mengikuti pesanan yang tidak bergantung pada penyajian kode), dan yang tidak memiliki cara lain untuk mewakili komposisi berurutan. Hasil bersihnya adalah bahwa bagian-bagian kode yang "tidak murni" (yaitu yang memang memiliki efek samping) dapat disajikan secara alami, dengan cara yang imperatif, namun dipisahkan secara bersih dari fungsi murni (tanpa efek samping), yang dapat dievaluasi dengan malas.
Ini hanya satu aspek, seperti yang diperingatkan di sini .
sumber
Penjelasan paling sederhana yang dapat saya pikirkan adalah bahwa monad adalah cara menyusun fungsi dengan hasil hiasan (alias komposisi Kleisli). Fungsi "embelished" memiliki tanda tangan di
a -> (b, smth)
manaa
danb
tipe (berpikirInt
,Bool
) yang mungkin berbeda satu sama lain, tetapi tidak harus - dansmth
merupakan "konteks" atau "embelishment".Jenis fungsi ini juga dapat ditulis di
a -> m b
manam
setara dengan "embelishment"smth
. Jadi ini adalah fungsi yang mengembalikan nilai dalam konteks (pikirkan fungsi yang mencatat tindakan mereka, di manasmth
pesan logging; atau fungsi yang melakukan input \ output dan hasilnya tergantung pada hasil dari tindakan IO).Monad adalah antarmuka ("typeclass") yang membuat implementer memberi tahu cara menyusun fungsi tersebut. Implementer perlu mendefinisikan fungsi komposisi
(a -> m b) -> (b -> m c) -> (a -> m c)
untuk semua jenism
yang ingin mengimplementasikan antarmuka (ini adalah komposisi Kleisli).Jadi, jika kita mengatakan bahwa kita memiliki tipe tuple yang
(Int, String)
mewakili hasil perhitungan padaInt
s yang juga mencatat tindakan mereka, dengan(_, String)
menjadi "embelishment" - log dari tindakan - dan dua fungsiincrement :: Int -> (Int, String)
dantwoTimes :: Int -> (Int, String)
kami ingin mendapatkan fungsiincrementThenDouble :: Int -> (Int, String)
yang merupakan komposisi dari dua fungsi yang juga memperhitungkan log.Pada contoh yang diberikan, implementasi monad dari dua fungsi berlaku untuk nilai integer 2
incrementThenDouble 2
(yang sama dengantwoTimes (increment 2)
) akan kembali(6, " Adding 1. Doubling 3.")
untuk hasil perantaraincrement 2
sama dengan(3, " Adding 1.")
dantwoTimes 3
sama dengan(6, " Doubling 3.")
Dari fungsi komposisi Kleisli ini seseorang dapat memperoleh fungsi monadik yang biasa.
sumber