Setelah secara singkat melihat Haskell baru-baru ini, apa yang akan menjadi penjelasan singkat, ringkas, praktis tentang apa dasarnya monad?
Saya telah menemukan sebagian besar penjelasan yang saya temui tidak masuk akal dan kurang detail praktis.
haskell
functional-programming
monads
terminology
Peter Mortensen
sumber
sumber
Jawaban:
Pertama: Istilah monad agak kosong jika Anda bukan ahli matematika. Istilah alternatif adalah pembangun perhitungan yang sedikit lebih deskriptif dari apa yang sebenarnya berguna bagi mereka.
Anda meminta contoh-contoh praktis:
Contoh 1: Pemahaman daftar :
Ungkapan ini mengembalikan ganda dari semua angka ganjil dalam kisaran dari 1 hingga 10. Sangat berguna!
Ternyata ini benar-benar hanya gula sintaksis untuk beberapa operasi dalam daftar monad. Pemahaman daftar yang sama dapat ditulis sebagai:
Atau bahkan:
Contoh 2: Input / Output :
Kedua contoh menggunakan monads, pembangun perhitungan AKA. Tema umum adalah bahwa rantai monad beroperasi dalam beberapa cara yang spesifik dan bermanfaat. Dalam pemahaman daftar, operasi dirantai sedemikian sehingga jika operasi mengembalikan daftar, maka operasi berikut dilakukan pada setiap item dalam daftar. Mono IO di sisi lain melakukan operasi secara berurutan, tetapi melewati "variabel tersembunyi" bersama, yang mewakili "keadaan dunia", yang memungkinkan kita untuk menulis kode I / O dengan cara fungsional murni.
Ternyata pola operasi chaining cukup berguna dan digunakan untuk banyak hal berbeda di Haskell.
Contoh lain adalah pengecualian: Menggunakan
Error
monad, operasi dirantai sedemikian rupa sehingga dilakukan secara berurutan, kecuali jika kesalahan dilemparkan, dalam hal ini sisa rantai ditinggalkan.Sintaksis daftar-pahami dan do-notasi adalah gula sintaksis untuk operasi perangkaian menggunakan
>>=
operator. Monad pada dasarnya hanya tipe yang mendukung>>=
operator.Contoh 3: Pengurai
Ini adalah pengurai yang sangat sederhana yang mem-parsing string yang dikutip atau angka:
Operasi
char
,,digit
dll. Cukup sederhana. Mereka cocok atau tidak cocok. Sihir adalah monad yang mengelola aliran kontrol: Operasi dilakukan secara berurutan hingga pertandingan gagal, dalam hal ini monad akan mundur ke yang terbaru<|>
dan mencoba opsi berikutnya. Sekali lagi, cara merantai operasi dengan beberapa semantik tambahan yang bermanfaat.Contoh 4: Pemrograman asinkron
Contoh di atas ada di Haskell, tetapi ternyata F # juga mendukung monads. Contoh ini dicuri dari Don Syme :
Metode ini mengambil halaman web. Punch line adalah penggunaan
GetResponseAsync
- ia benar-benar menunggu respons pada utas terpisah, sedangkan utas utama kembali dari fungsi. Tiga baris terakhir dieksekusi pada utas menelurkan ketika respons telah diterima.Dalam sebagian besar bahasa lain Anda harus secara eksplisit membuat fungsi terpisah untuk baris yang menangani respons. The
async
monad mampu "split" blok sendiri dan menunda pelaksanaan paruh kedua. (async {}
Sintaks menunjukkan bahwa aliran kontrol di blok ditentukan olehasync
monad.)Bagaimana mereka bekerja
Jadi, bagaimana bisa monad melakukan semua hal aliran kendali yang mewah ini? Apa yang sebenarnya terjadi pada do-block (atau ekspresi komputasi sebagaimana mereka disebut dalam F #), adalah bahwa setiap operasi (pada dasarnya setiap baris) dibungkus dalam fungsi anonim yang terpisah. Fungsi-fungsi ini kemudian digabungkan menggunakan
bind
operator (dieja>>=
dalam Haskell). Karenabind
operasi menggabungkan fungsi, ia dapat menjalankannya sesuai keinginan: secara berurutan, beberapa kali, secara terbalik, membuang beberapa, mengeksekusi beberapa di utas terpisah ketika terasa seperti itu dan seterusnya.Sebagai contoh, ini adalah versi diperluas dari kode-IO dari contoh 2:
Ini lebih buruk, tetapi juga lebih jelas apa yang sebenarnya terjadi. The
>>=
operator adalah bahan ajaib: Dibutuhkan nilai (di sisi kiri) dan menggabungkan dengan fungsi (di sisi kanan), untuk menghasilkan nilai baru. Nilai baru ini kemudian diambil oleh>>=
operator berikutnya dan sekali lagi dikombinasikan dengan fungsi untuk menghasilkan nilai baru.>>=
dapat dilihat sebagai evaluator mini.Catatan yang
>>=
kelebihan beban untuk berbagai jenis, sehingga setiap monad memiliki implementasi sendiri>>=
. (Semua operasi dalam rantai harus dari jenis monad yang sama, jika tidak,>>=
operator tidak akan bekerja.)Implementasi yang paling sederhana dari
>>=
hanya mengambil nilai di sebelah kiri dan menerapkannya pada fungsi di sebelah kanan dan mengembalikan hasilnya, tetapi seperti yang dikatakan sebelumnya, apa yang membuat seluruh pola berguna adalah ketika ada sesuatu yang terjadi dalam implementasi monad dari>>=
.Ada beberapa kepintaran tambahan dalam bagaimana nilai-nilai dilewatkan dari satu operasi ke operasi berikutnya, tetapi ini membutuhkan penjelasan yang lebih dalam dari sistem tipe Haskell.
Menyimpulkan
Dalam istilah Haskell, monad adalah tipe parameter yang merupakan turunan dari kelas tipe Monad, yang mendefinisikan
>>=
bersama dengan beberapa operator lainnya. Dalam istilah awam, monad hanyalah tipe>>=
operasi yang didefinisikan.Dalam dirinya sendiri
>>=
hanyalah cara rumit dari fungsi chaining, tetapi dengan kehadiran notasi yang menyembunyikan "plumbing", operasi monadik ternyata menjadi abstraksi yang sangat bagus dan berguna, berguna banyak tempat dalam bahasa, dan berguna untuk membuat bahasa mini Anda sendiri dalam bahasa tersebut.Mengapa monad sulit?
Bagi banyak pelajar Haskell, monad adalah hambatan yang mereka pukul seperti dinding bata. Bukannya monad itu sendiri kompleks, tetapi implementasinya bergantung pada banyak fitur Haskell canggih lainnya seperti tipe parameter, kelas tipe, dan sebagainya. Masalahnya adalah bahwa Haskell I / O didasarkan pada monad, dan I / O mungkin adalah salah satu hal pertama yang ingin Anda pahami ketika mempelajari bahasa baru - lagipula, tidak terlalu menyenangkan untuk membuat program yang tidak menghasilkan apa-apa keluaran. Saya tidak punya solusi langsung untuk masalah ayam-dan-telur ini, kecuali memperlakukan saya / O seperti "keajaiban terjadi di sini" sampai Anda memiliki cukup pengalaman dengan bagian bahasa lain. Maaf.
Blog luar biasa di monads: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
sumber
Menjelaskan "apa itu monad" agak mirip dengan mengatakan "apa itu angka?" Kami menggunakan angka setiap saat. Tapi bayangkan Anda bertemu seseorang yang tidak tahu apa-apa tentang angka. Bagaimana sih Anda akan menjelaskan apa nomor yang? Dan bagaimana Anda bahkan mulai menjelaskan mengapa itu mungkin berguna?
Apa itu monad? Jawaban singkatnya: Ini adalah cara khusus untuk merantai operasi bersama.
Intinya, Anda menulis langkah-langkah eksekusi dan menghubungkannya bersama dengan "fungsi bind". (Dalam Haskell, ini dinamai
>>=
.) Anda dapat menulis sendiri panggilan ke operator bind, atau Anda dapat menggunakan gula sintaks yang membuat kompiler memasukkan fungsi panggilan tersebut untuk Anda. Namun demikian, setiap langkah dipisahkan oleh panggilan ke fungsi bind ini.Jadi fungsi ikatnya seperti titik koma; itu memisahkan langkah-langkah dalam suatu proses. Tugas fungsi bind adalah mengambil output dari langkah sebelumnya, dan memasukkannya ke langkah berikutnya.
Itu tidak terdengar terlalu sulit, bukan? Tetapi ada lebih dari satu jenis monad. Mengapa? Bagaimana?
Nah, fungsi bind bisa mengambil hasil dari satu langkah, dan memasukkannya ke langkah berikutnya. Tetapi jika itu "semua" yang dilakukan monad ... itu sebenarnya tidak terlalu berguna. Dan itu penting untuk dipahami: Setiap monad bermanfaat melakukan hal lain selain hanya menjadi monad. Setiap monad yang bermanfaat memiliki "kekuatan khusus", yang membuatnya unik.
(Monad yang tidak melakukan apa - apa istimewa disebut "identitas monad". Agak seperti fungsi identitas, ini terdengar seperti hal yang sama sekali tidak ada gunanya, namun ternyata bukan ... Tapi itu cerita lain ™.)
Pada dasarnya, setiap monad memiliki implementasi fungsi bind sendiri. Dan Anda dapat menulis fungsi bind sedemikian rupa sehingga tidak terjadi apa-apa di antara langkah-langkah eksekusi. Sebagai contoh:
Jika setiap langkah mengembalikan indikator keberhasilan / kegagalan, Anda dapat mengikat menjalankan langkah berikutnya hanya jika yang sebelumnya berhasil. Dengan cara ini, langkah yang gagal membatalkan seluruh urutan "secara otomatis", tanpa pengujian bersyarat dari Anda. (The Failure Monad .)
Memperluas gagasan ini, Anda dapat menerapkan "pengecualian". ( Monad Kesalahan atau Monad Pengecualian .) Karena Anda mendefinisikannya sendiri alih-alih sebagai fitur bahasa, Anda dapat menentukan cara kerjanya. (Misalnya, mungkin Anda ingin mengabaikan dua pengecualian pertama dan hanya membatalkan ketika pengecualian ketiga dilemparkan.)
Anda dapat membuat setiap langkah mengembalikan beberapa hasil , dan meminta fungsi simpul diikat, memberi makan masing-masing ke langkah berikutnya untuk Anda. Dengan cara ini, Anda tidak harus terus menulis loop di semua tempat ketika berhadapan dengan banyak hasil. Fungsi bind "secara otomatis" melakukan semua itu untuk Anda. ( Daftar Monad .)
Selain meneruskan "hasil" dari satu langkah ke langkah lainnya, Anda dapat memiliki fungsi bind untuk meneruskan data tambahan juga. Data ini sekarang tidak muncul dalam kode sumber Anda, tetapi Anda masih dapat mengaksesnya dari mana saja, tanpa harus meneruskannya secara manual ke setiap fungsi. (The Reader Monad .)
Anda dapat membuatnya sehingga "data tambahan" dapat diganti. Ini memungkinkan Anda untuk mensimulasikan pembaruan yang merusak , tanpa benar-benar melakukan pembaruan yang merusak. (The State Monad dan sepupunya Writer Monad .)
Karena Anda hanya mensimulasikan pembaruan destruktif, Anda dapat melakukan hal-hal sepele yang tidak mungkin dilakukan dengan pembaruan destruktif nyata . Misalnya, Anda dapat membatalkan pembaruan terakhir , atau kembali ke versi yang lebih lama .
Anda dapat membuat monad tempat penghitungan dapat dijeda , sehingga Anda dapat menjeda program Anda, masuk dan mengotak-atik data keadaan internal, dan kemudian melanjutkannya.
Anda dapat menerapkan "kelanjutan" sebagai monad. Ini memungkinkan Anda untuk menghancurkan pikiran orang!
Semua ini dan banyak lagi dimungkinkan dengan monad. Tentu saja, semua ini juga sangat mungkin terjadi tanpa monad juga. Ini hanya secara drastis lebih mudah menggunakan monad.
sumber
Sebenarnya, bertentangan dengan pemahaman umum tentang Monads, mereka tidak ada hubungannya dengan negara. Monads hanyalah cara untuk membungkus barang-barang dan menyediakan metode untuk melakukan operasi pada barang yang dibungkus tanpa membuka bungkusnya.
Misalnya, Anda bisa membuat tipe untuk membungkus yang lain, di Haskell:
Untuk membungkus barang yang kami tentukan
Untuk melakukan operasi tanpa membuka, katakan Anda memiliki fungsi
f :: a -> b
, maka Anda dapat melakukan ini untuk mengangkat fungsi itu untuk bertindak pada nilai yang dibungkus:Hanya itu yang perlu dipahami. Namun, ternyata ada fungsi yang lebih umum untuk melakukan pengangkatan ini , yaitu
bind
:bind
dapat melakukan sedikit lebih banyak daripadafmap
, tetapi tidak sebaliknya. Sebenarnya,fmap
dapat didefinisikan hanya dari segibind
danreturn
. Jadi, ketika mendefinisikan sebuah monad .. Anda memberikan tipenya (ini diaWrapped a
) dan kemudian mengatakan bagaimana itureturn
danbind
operasi bekerja.Yang keren adalah bahwa ini ternyata menjadi pola umum sehingga muncul di semua tempat, keadaan enkapsulasi dengan cara murni hanya salah satunya.
Untuk artikel yang bagus tentang bagaimana monad dapat digunakan untuk memperkenalkan dependensi fungsional dan dengan demikian mengontrol urutan evaluasi, seperti yang digunakan dalam monad IO Haskell, lihat IO Inside .
Sedangkan untuk memahami monad, jangan terlalu khawatir tentang hal itu. Baca tentang mereka apa yang menurut Anda menarik dan jangan khawatir jika Anda tidak segera mengerti. Maka hanya menyelam dalam bahasa seperti Haskell adalah cara untuk pergi. Monad adalah salah satu dari hal-hal ini di mana pemahaman mengalir ke otak Anda dengan latihan, suatu hari Anda tiba-tiba menyadari Anda memahaminya.
sumber
Tapi, Anda bisa menemukan Monads!
sumber
Monad adalah tipe data yang memiliki dua operasi:
>>=
(aliasbind
) danreturn
(aliasunit
).return
mengambil nilai sewenang-wenang dan menciptakan instance monad dengannya.>>=
mengambil instance dari monad dan memetakan fungsi di atasnya. (Anda sudah dapat melihat bahwa monad adalah sejenis tipe data yang aneh, karena di sebagian besar bahasa pemrograman Anda tidak dapat menulis fungsi yang mengambil nilai arbitrer dan membuat jenis darinya. Monad menggunakan sejenis polimorfisme parametrik .)Dalam notasi Haskell, antarmuka monad ditulis
Operasi-operasi ini seharusnya mematuhi "undang-undang" tertentu, tetapi itu tidak terlalu penting: "undang-undang" hanya mengkodifikasikan cara pelaksanaan operasi yang masuk akal harus berperilaku (pada dasarnya, itu
>>=
danreturn
harus setuju tentang bagaimana nilai-nilai ditransformasikan menjadi contoh monad dan itu>>=
asosiatif).Monad bukan hanya tentang negara dan I / O: mereka abstrak pola komputasi yang umum yang mencakup bekerja dengan negara, I / O, pengecualian, dan non-determinisme. Mungkin monad paling sederhana untuk dipahami adalah daftar dan jenis opsi:
di mana
[]
dan:
adalah konstruktor daftar,++
adalah operator gabungan, danJust
danNothing
adalahMaybe
konstruktor. Kedua monad ini merangkum pola komputasi yang umum dan berguna pada masing-masing tipe data (perhatikan bahwa tidak ada hubungannya dengan efek samping atau I / O).Anda benar-benar harus bermain-main menulis beberapa kode Haskell non-sepele untuk menghargai tentang apa itu monad dan mengapa mereka berguna.
sumber
Anda harus terlebih dahulu memahami apa itu functor. Sebelum itu, pahami fungsi tingkat tinggi.
Fungsi tingkat tinggi hanyalah fungsi yang mengambil fungsi sebagai argumen.
Sebuah functor adalah setiap konstruksi jenis
T
yang terdapat fungsi tingkat tinggi, sebut sajamap
, bahwa transformasi fungsi dari jenisa -> b
(diberi dua jenisa
danb
) ke dalam fungsiT a -> T b
.map
Fungsi ini juga harus mematuhi hukum-hukum identitas dan komposisi sehingga ungkapan berikut ini berlaku untuk semuap
danq
(notasi Haskell):Misalnya, tipe konstruktor yang dipanggil
List
adalah functor jika dilengkapi dengan fungsi tipe(a -> b) -> List a -> List b
yang mematuhi hukum di atas. Satu-satunya implementasi praktis sudah jelas. Fungsi yang dihasilkanList a -> List b
berulang di atas daftar yang diberikan, memanggil(a -> b)
fungsi untuk setiap elemen, dan mengembalikan daftar hasil.Sebuah monad dasarnya hanya functor
T
dengan dua metode ekstra,join
, jenisT (T a) -> T a
, danunit
(kadang-kadang disebutreturn
,fork
ataupure
) tipea -> T a
. Untuk daftar di Haskell:Mengapa itu bermanfaat? Karena Anda bisa, misalnya,
map
pada daftar dengan fungsi yang mengembalikan daftar.Join
mengambil daftar daftar yang dihasilkan dan menggabungkannya.List
adalah monad karena ini mungkin.Anda dapat menulis fungsi yang berfungsi
map
, kemudianjoin
. Fungsi ini disebutbind
, atauflatMap
, atau(>>=)
, atau(=<<)
. Ini biasanya bagaimana instance monad diberikan dalam Haskell.Monad harus memenuhi hukum tertentu, yaitu yang
join
harus asosiatif. Ini berarti bahwa jika Anda memiliki nilaix
tipe[[[a]]]
makajoin (join x)
harus samajoin (map join x)
. Danpure
harus menjadi identitas untukjoin
itujoin (pure x) == x
.sumber
[Penafian: Saya masih mencoba sepenuhnya grok monad. Berikut ini adalah apa yang saya pahami sejauh ini. Jika itu salah, semoga seseorang yang berpengetahuan akan memanggilku di karpet.]
Arnar menulis:
Tepat seperti itu. Idenya seperti ini:
Anda mengambil semacam nilai dan membungkusnya dengan beberapa informasi tambahan. Sama seperti nilainya dari jenis tertentu (mis. Integer atau string), sehingga informasi tambahan dari jenis tertentu.
Misalnya, informasi tambahan itu mungkin a
Maybe
atau aIO
.Kemudian Anda memiliki beberapa operator yang memungkinkan Anda untuk beroperasi pada data yang dibungkus sambil membawa informasi tambahan itu. Operator-operator ini menggunakan informasi tambahan untuk memutuskan bagaimana mengubah perilaku operasi pada nilai yang dibungkus.
Misalnya, a
Maybe Int
bisa aJust Int
atauNothing
. Sekarang, jika Anda menambahkan aMaybe Int
keMaybe Int
, operator akan memeriksa untuk melihat apakah keduanya adaJust Int
di dalam, dan jika demikian, akan membuka bungkusnyaInt
, meneruskannya ke operator tambahan, membungkus ulang hasilnyaInt
menjadi yang baruJust Int
(yang validMaybe Int
), dan dengan demikian mengembalikan aMaybe Int
. Tetapi jika salah satunya adaNothing
di dalam, operator ini akan langsung kembaliNothing
, yang lagi adalah validMaybe Int
. Dengan begitu, Anda dapat berpura-pura bahwaMaybe Int
angka Anda hanya normal dan melakukan matematika reguler pada angka itu. Jika Anda mendapatkanNothing
, persamaan Anda masih akan menghasilkan hasil yang tepat - tanpa Anda harus membuang cek untuk diNothing
mana - mana .Tetapi contohnya adalah apa yang terjadi
Maybe
. Jika informasi tambahan adalahIO
, maka operator khusus yang ditentukan untukIO
s akan dipanggil, dan itu bisa melakukan sesuatu yang sama sekali berbeda sebelum melakukan penambahan. (Oke, menambahkan duaIO Int
s bersama-sama mungkin tidak masuk akal - saya belum yakin.) (Juga, jika Anda memperhatikanMaybe
contoh, Anda telah memperhatikan bahwa "membungkus nilai dengan barang tambahan" tidak selalu benar. Tetapi sulit tepatnya, benar, dan tepat tanpa bisa dipahami.)Pada dasarnya, "monad" secara kasar berarti "pola" . Tetapi alih-alih sebuah buku yang penuh dengan Pola yang dijelaskan secara informal dan secara spesifik bernama, Anda sekarang memiliki konstruk bahasa - sintaks dan semua - yang memungkinkan Anda untuk mendeklarasikan pola baru sebagai hal dalam program Anda . (Ketidaktepatan di sini adalah semua pola harus mengikuti bentuk tertentu, jadi monad tidak cukup generik seperti pola. Tapi saya pikir itu istilah terdekat yang kebanyakan orang tahu dan mengerti.)
Dan itulah mengapa orang menganggap monad sangat membingungkan: karena mereka adalah konsep yang sangat umum. Untuk bertanya apa yang membuat sesuatu menjadi monad sama tidak jelasnya dengan bertanya apa yang membuat sesuatu menjadi suatu pola.
Tapi pikirkan implikasi dari memiliki dukungan sintaksis dalam bahasa untuk ide pola: daripada harus membaca buku Gang of Four dan menghafal konstruksi pola tertentu, Anda hanya menulis kode yang mengimplementasikan pola ini dalam agnostik, cara umum sekali dan kemudian Anda selesai! Anda kemudian dapat menggunakan kembali pola ini, seperti Pengunjung atau Strategi atau Façade atau apa pun, hanya dengan mendekorasi operasi dalam kode Anda dengannya, tanpa harus menerapkannya berulang kali!
Jadi itu sebabnya orang yang memahami monad menganggapnya sangat berguna : itu bukan konsep menara gading yang sombong intelektual bangga pada pemahaman (OK, tentu saja, teehee), tetapi sebenarnya membuat kode lebih sederhana.
sumber
M (M a) -> M a
. Fakta bahwa Anda dapat mengubahnya menjadi salah satu jenisM a -> (a -> M b) -> M b
adalah apa yang membuatnya berguna.Setelah banyak berjuang, saya pikir saya akhirnya mengerti monad. Setelah membaca ulang kritik saya yang panjang tentang jawaban yang terpilih paling banyak, saya akan menawarkan penjelasan ini.
Ada tiga pertanyaan yang perlu dijawab untuk memahami monad:
Seperti yang saya catat dalam komentar asli saya, terlalu banyak penjelasan monad terperangkap dalam pertanyaan nomor 3, tanpa, dan sebelum benar-benar cukup mencakup pertanyaan 2, atau pertanyaan 1.
Mengapa Anda membutuhkan monad?
Bahasa fungsional murni seperti Haskell berbeda dari bahasa imperatif seperti C, atau Java dalam hal itu, program fungsional murni tidak harus dijalankan dalam urutan tertentu, satu langkah pada satu waktu. Program Haskell lebih mirip dengan fungsi matematika, di mana Anda dapat menyelesaikan "persamaan" di sejumlah pesanan potensial. Ini memberi sejumlah manfaat, di antaranya adalah menghilangkan kemungkinan jenis bug tertentu, terutama yang berkaitan dengan hal-hal seperti "keadaan".
Namun, ada masalah tertentu yang tidak begitu mudah dipecahkan dengan gaya pemrograman ini. Beberapa hal, seperti pemrograman konsol, dan file i / o, memerlukan hal-hal yang terjadi dalam urutan tertentu, atau perlu mempertahankan keadaan. Salah satu cara untuk mengatasi masalah ini adalah dengan membuat semacam objek yang mewakili keadaan komputasi, dan serangkaian fungsi yang mengambil objek keadaan sebagai input, dan mengembalikan objek keadaan yang dimodifikasi baru.
Jadi mari kita buat nilai "status" hipotetis, yang mewakili kondisi layar konsol. persis bagaimana nilai ini dibangun tidak penting, tetapi katakanlah itu adalah array byte panjang karakter ascii yang mewakili apa yang saat ini terlihat di layar, dan array yang mewakili baris input terakhir yang dimasukkan oleh pengguna, dalam pseudocode. Kami telah menetapkan beberapa fungsi yang mengambil status konsol, memodifikasinya, dan mengembalikan status konsol baru.
Jadi untuk melakukan pemrograman konsol, tetapi dengan cara fungsional murni, Anda perlu membuat banyak panggilan fungsi di dalam satu sama lain.
Pemrograman dengan cara ini menjaga gaya fungsional "murni", sementara memaksa perubahan pada konsol terjadi dalam urutan tertentu. Tetapi, kami mungkin ingin melakukan lebih dari sekedar beberapa operasi pada waktu seperti pada contoh di atas. Fungsi bersarang dengan cara itu akan mulai menjadi canggung. Apa yang kita inginkan, adalah kode yang pada dasarnya melakukan hal yang sama seperti di atas, tetapi ditulis lebih seperti ini:
Ini memang akan menjadi cara yang lebih nyaman untuk menulisnya. Bagaimana kita melakukannya?
Apa itu monad?
Setelah Anda memiliki tipe (seperti
consolestate
) yang Anda tentukan bersama dengan banyak fungsi yang dirancang khusus untuk beroperasi pada tipe itu, Anda dapat mengubah seluruh paket hal-hal ini menjadi "monad" dengan mendefinisikan operator seperti:
(bind) yang secara otomatis mengumpankan nilai kembali di sebelah kirinya, ke dalam parameter fungsi di sebelah kanannya, danlift
operator yang mengubah fungsi normal, menjadi fungsi yang bekerja dengan operator pengikat jenis tertentu itu.Bagaimana monad diimplementasikan?
Lihat jawaban lain, yang tampaknya cukup bebas untuk melompat ke detail itu.
sumber
Setelah memberikan jawaban untuk pertanyaan ini beberapa tahun yang lalu, saya percaya saya dapat meningkatkan dan menyederhanakan tanggapan itu dengan ...
Monad adalah teknik komposisi fungsi yang mengeksternalkan pengobatan untuk beberapa skenario input menggunakan fungsi penulisan
bind
, untuk memproses input sebelum proses komposisi.Dalam komposisi normal, fungsi,,
compose (>>)
digunakan untuk menerapkan fungsi yang tersusun pada hasil pendahulunya secara berurutan. Yang penting, fungsi yang sedang disusun diperlukan untuk menangani semua skenario inputnya.(x -> y) >> (y -> z)
Desain ini dapat ditingkatkan dengan merestrukturisasi input sehingga keadaan yang relevan lebih mudah diinterogasi. Jadi, alih-alih hanya
y
nilai dapat menjadiMb
seperti, misalnya,(is_OK, b)
jikay
menyertakan gagasan validitas.Misalnya, ketika input hanya berupa angka, alih-alih mengembalikan string yang dapat berisi dengan patuh berisi angka atau tidak, Anda bisa merestrukturisasi jenisnya menjadi yang
bool
menunjukkan keberadaan nomor yang valid dan angka dalam tupel sepertibool * float
,. Fungsi-fungsi yang dikomposisikan sekarang tidak lagi perlu mengurai string input untuk menentukan apakah suatu angka ada tetapi hanya dapat memeriksabool
bagian dari sebuah tuple.(Ma -> Mb) >> (Mb -> Mc)
Di sini, sekali lagi, komposisi terjadi secara alami
compose
dan karenanya setiap fungsi harus menangani semua skenario inputnya secara individual, meskipun melakukannya sekarang jauh lebih mudah.Namun, bagaimana jika kita bisa mengeksternalisasi upaya interogasi untuk saat-saat di mana menangani skenario adalah rutin. Sebagai contoh, bagaimana jika program kami tidak melakukan apa-apa saat input tidak OK seperti pada saat
is_OK
itufalse
. Jika itu dilakukan maka fungsi yang dikomposisikan tidak perlu menangani skenario itu sendiri, secara dramatis menyederhanakan kode mereka dan memengaruhi tingkat penggunaan kembali yang lain.Untuk mencapai eksternalisasi ini, kita dapat menggunakan fungsi
bind (>>=)
,, untuk melakukancomposition
alih - alihcompose
. Dengan demikian, alih-alih hanya mentransfer nilai dari output satu fungsi ke input yang lainBind
akan memeriksaM
bagianMa
dan memutuskan apakah dan bagaimana menerapkan fungsi yang dikomposisikan kea
. Tentu saja, fungsibind
akan didefinisikan secara khusus untuk kitaM
sehingga dapat memeriksa strukturnya dan melakukan apa pun jenis aplikasi yang kita inginkan. Meskipun demikian,a
dapat berupa apa saja karenabind
hanya meneruskan yang tidaka
terinspeksi ke fungsi yang dikomposisikan ketika itu menentukan aplikasi yang diperlukan. Selain itu, fungsi yang dikomposisikan sendiri tidak perlu lagi berurusan denganM
sebagian dari struktur input baik, menyederhanakannya. Karenanya...(a -> Mb) >>= (b -> Mc)
atau lebih ringkasMb >>= (b -> Mc)
Singkatnya, sebuah monad mengeksternalisasi dan dengan demikian memberikan perilaku standar seputar penanganan skenario input tertentu begitu input dirancang untuk mengeksposnya secara memadai. Desain ini adalah
shell and content
model di mana shell berisi data yang relevan dengan penerapan fungsi yang disusun dan diinterogasi oleh dan tetap hanya tersedia untukbind
fungsi tersebut.Karena itu, monad adalah tiga hal:
M
shell untuk menyimpan informasi yang relevan monad,bind
fungsi yang diimplementasikan untuk memanfaatkan informasi shell ini dalam penerapan fungsi yang dikomposisikan ke nilai konten yang ditemukannya di dalam shell, dana -> Mb
,, menghasilkan hasil yang mencakup data manajemen monadik.Secara umum, input ke suatu fungsi jauh lebih ketat daripada outputnya yang dapat mencakup hal-hal seperti kondisi kesalahan; karenanya,
Mb
struktur hasil umumnya sangat bermanfaat. Misalnya, operator divisi tidak mengembalikan nomor ketika pembagi itu0
.Selain itu,
monad
s dapat mencakup fungsi bungkus yang membungkus nilai,,a
ke dalam jenis monadikMa
,, dan fungsi umuma -> b
,, ke dalam fungsi monadika -> Mb
,, dengan membungkus hasil mereka setelah aplikasi. Tentu saja, sepertibind
, fungsi bungkus seperti itu khusus untukM
. Sebuah contoh:Desain
bind
fungsi mengandaikan struktur data yang tidak dapat diubah dan fungsi murni yang lainnya menjadi kompleks dan jaminan tidak dapat dibuat. Karena itu, ada hukum monadik:Diberikan ...
Kemudian...
Associativity
berartibind
mempertahankan urutan evaluasi terlepas dari kapanbind
diterapkan. Artinya, dalam definisiAssociativity
di atas, gaya evaluasi awal kurungbinding
darif
dang
hanya akan menghasilkan fungsi yang mengharapkanMa
untuk menyelesaikanbind
. Oleh karena itu evaluasiMa
harus ditentukan sebelum nilainya dapat diterapkanf
dan hasilnya pada gilirannya diterapkang
.sumber
Monad adalah, secara efektif, bentuk "operator tipe". Itu akan melakukan tiga hal. Pertama itu akan "membungkus" (atau mengkonversi) nilai dari satu jenis ke jenis lain (biasanya disebut "tipe monadik"). Kedua itu akan membuat semua operasi (atau fungsi) tersedia pada tipe yang mendasarinya tersedia pada tipe monadik. Akhirnya akan memberikan dukungan untuk menggabungkan diri dengan monad lain untuk menghasilkan monad komposit.
"Mungkin monad" pada dasarnya setara dengan "tipe yang dapat dibatalkan" dalam Visual Basic / C #. Dibutuhkan tipe "T" yang tidak dapat dibatalkan dan mengubahnya menjadi "Nullable <T>", dan kemudian mendefinisikan apa arti semua operator biner pada Nullable <T>.
Efek samping terwakili secara bersamaan. Struktur dibuat yang menyimpan deskripsi efek samping di samping nilai pengembalian fungsi. Operasi "terangkat" kemudian menyalin efek samping saat nilai melewati fungsi.
Mereka disebut "monad" daripada nama "operator tipe" yang lebih mudah dipahami karena beberapa alasan:
sumber
(Lihat juga jawaban di What is a monad? )
Motivasi yang baik untuk Monads adalah sigfpe (Dan Piponi), You Could Have Invented Monads! (Dan Mungkin Anda Sudah Memiliki) . Ada BANYAK tutorial monad lainnya , banyak di antaranya secara keliru mencoba menjelaskan monad dalam "istilah sederhana" menggunakan berbagai analogi: ini adalah kesalahan tutorial monad ; Hindari mereka.
Seperti yang dikatakan DR MacIver dalam Beri tahu kami mengapa bahasa Anda payah :
Anda mengatakan Anda mengerti Mungkin monad? Bagus, Anda sedang dalam perjalanan. Mulailah menggunakan monad lain dan cepat atau lambat Anda akan mengerti apa itu monad secara umum.
[Jika Anda berorientasi secara matematis, Anda mungkin ingin mengabaikan lusinan tutorial dan mempelajari definisi, atau mengikuti kuliah dalam teori kategori :) Bagian utama dari definisi adalah bahwa Monad M melibatkan "konstruktor tipe" yang mendefinisikan untuk masing-masing tipe yang ada "T" tipe baru "MT", dan beberapa cara untuk bolak-balik antara tipe "biasa" dan tipe "M".]
Juga, cukup mengejutkan, salah satu pengantar terbaik untuk monad sebenarnya adalah salah satu makalah akademis awal yang memperkenalkan monad, Monad karya Philip Wadler untuk pemrograman fungsional . Ini sebenarnya memiliki contoh-contoh yang praktis dan memotivasi non-sepele , tidak seperti banyak tutorial buatan di luar sana.
sumber
Monads adalah untuk mengontrol aliran apa tipe data abstrak untuk data.
Dengan kata lain, banyak pengembang merasa nyaman dengan gagasan Set, Daftar, Kamus (atau Hash, atau Maps), dan Trees. Di dalam tipe data tersebut ada banyak kasus khusus (misalnya InsertionOrderPreservingIdentityHashMap).
Namun, ketika dihadapkan dengan "aliran" program banyak pengembang belum terkena lebih banyak konstruksi daripada jika, beralih / kasing, lakukan, sementara, goto (grr), dan (mungkin) penutupan.
Jadi, monad hanyalah sebuah konstruksi aliran kontrol. Ungkapan yang lebih baik untuk mengganti monad adalah 'tipe kontrol'.
Dengan demikian, sebuah monad memiliki slot untuk logika kontrol, atau pernyataan, atau fungsi - padanan dalam struktur data akan mengatakan bahwa beberapa struktur data memungkinkan Anda untuk menambahkan data, dan menghapusnya.
Misalnya, monad "jika":
paling sederhana memiliki dua slot - klausa, dan satu blok. The
if
monad biasanya dibangun untuk mengevaluasi hasil dari klausa, dan jika tidak salah, mengevaluasi blok. Banyak pengembang tidak diperkenalkan dengan monad ketika mereka belajar 'jika', dan tidak perlu memahami monad untuk menulis logika yang efektif.Monads dapat menjadi lebih rumit, dengan cara yang sama seperti struktur data dapat menjadi lebih rumit, tetapi ada banyak kategori luas monad yang mungkin memiliki semantik yang serupa, tetapi implementasi dan sintaksis berbeda.
Tentu saja, dengan cara yang sama bahwa struktur data dapat diulang, atau dilalui, monad dapat dievaluasi.
Compiler mungkin atau mungkin tidak memiliki dukungan untuk monad yang ditentukan pengguna. Haskell tentu saja melakukannya. Ioke memiliki beberapa kemampuan yang serupa, meskipun istilah monad tidak digunakan dalam bahasa tersebut.
sumber
Tutorial Monad favorit saya:
http://www.haskell.org/haskellwiki/All_About_Monads
(dari 170.000 klik pada pencarian Google untuk "tutorial monad"!)
@ Sat: Titik monad adalah untuk memungkinkan Anda untuk menambahkan (biasanya) semantik berurutan ke kode murni; Anda bahkan dapat membuat monad (menggunakan Monad Transformers) dan mendapatkan semantik gabungan yang lebih menarik dan rumit, seperti parsing dengan penanganan kesalahan, keadaan bersama, dan pencatatan, misalnya. Semua ini dimungkinkan dalam kode murni, monads hanya memungkinkan Anda untuk abstrak itu dan menggunakannya kembali di perpustakaan modular (selalu baik dalam pemrograman), serta memberikan sintaks yang mudah agar terlihat penting.
Haskell sudah mempunyai operator overloading [1]: ia menggunakan kelas tipe seperti halnya orang menggunakan antarmuka di Java atau C # tetapi Haskell kebetulan juga memperbolehkan token non-alfanumerik seperti + && dan> sebagai pengidentifikasi infiks. Ini hanya kelebihan operator dengan cara Anda melihatnya jika Anda bermaksud "membebani titik koma" [2]. Kedengarannya seperti ilmu hitam dan meminta masalah untuk "membebani titik koma" (peretas giat Perl mendapatkan ide ini) tetapi intinya adalah bahwa tanpa monad tidak ada tanda titik koma, karena kode fungsional murni tidak memerlukan atau memungkinkan sequencing eksplisit.
Ini semua terdengar jauh lebih rumit dari yang seharusnya. Artikel sigfpe cukup keren tetapi menggunakan Haskell untuk menjelaskannya, jenis kegagalan untuk memecahkan masalah ayam dan telur dalam memahami Haskell untuk grok Monads dan memahami Monads untuk grok Haskell.
[1] Ini adalah masalah terpisah dari monad tetapi monad menggunakan fitur kelebihan operator Haskell.
[2] Ini juga merupakan penyederhanaan yang berlebihan karena operator untuk merantai aksi monadik adalah >> = (diucapkan "mengikat") tetapi ada gula sintaksis ("lakukan") yang memungkinkan Anda menggunakan kawat gigi dan titik koma dan / atau lekukan dan baris baru.
sumber
Saya telah memikirkan Monads dengan cara yang berbeda, akhir-akhir ini. Saya telah memikirkan mereka sebagai abstrak urutan eksekusi dengan cara matematika, yang memungkinkan jenis polimorfisme baru menjadi mungkin.
Jika Anda menggunakan bahasa imperatif, dan Anda menulis beberapa ekspresi secara berurutan, kode SELALU berjalan persis dalam urutan itu.
Dan dalam kasus sederhana, ketika Anda menggunakan monad, rasanya sama - Anda mendefinisikan daftar ekspresi yang terjadi secara berurutan. Kecuali itu, tergantung pada monad mana yang Anda gunakan, kode Anda mungkin berjalan secara berurutan (seperti di IO monad), secara paralel atas beberapa item sekaligus (seperti dalam Daftar monad), mungkin berhenti di tengah jalan (seperti di Mungkin monad) , mungkin berhenti di tengah jalan untuk dilanjutkan kembali nanti (seperti dalam monad Pengembalian), ia mungkin mundur dan mulai dari awal (seperti dalam monad Transaksi), atau mungkin mundur sebagian untuk mencoba opsi lain (seperti dalam Logika monad) .
Dan karena monad bersifat polimorfik, Anda dapat menjalankan kode yang sama di monad yang berbeda, tergantung kebutuhan Anda.
Plus, dalam beberapa kasus, dimungkinkan untuk menggabungkan monad bersama-sama (dengan transformer monad) untuk mendapatkan beberapa fitur sekaligus.
sumber
Saya masih baru di monad, tetapi saya pikir saya akan membagikan tautan yang menurut saya terasa sangat enak dibaca (DENGAN GAMBAR !!): http://www.matusiak.eu/numerodix/blog/2012/3/11/ monads-for-the-awam / (tidak ada afiliasi)
Pada dasarnya, konsep hangat dan tidak jelas yang saya dapatkan dari artikel ini adalah konsep bahwa monad pada dasarnya adalah adaptor yang memungkinkan fungsi yang berbeda untuk bekerja dengan cara yang dapat digubah, yaitu dapat merangkai beberapa fungsi dan mencampur dan mencocokkannya tanpa khawatir tentang pengembalian yang tidak konsisten. jenis dan semacamnya. Jadi fungsi BIND bertugas menjaga apel dengan apel dan jeruk dengan jeruk ketika kita mencoba membuat adaptor ini. Dan fungsi LIFT bertugas mengambil fungsi "level bawah" dan "memutakhirkan" mereka untuk bekerja dengan fungsi BIND dan dapat dikomposasikan juga.
Saya harap saya melakukannya dengan benar, dan yang lebih penting, berharap artikel tersebut memiliki pandangan yang valid tentang monad. Jika tidak ada yang lain, artikel ini membantu membangkitkan selera saya untuk belajar lebih banyak tentang Monad.
sumber
Selain jawaban yang sangat baik di atas, izinkan saya menawarkan Anda tautan ke artikel berikut (oleh Patrick Thomson) yang menjelaskan monad dengan menghubungkan konsep ke pustaka JavaScript jQuery (dan caranya menggunakan "metode perangkaian" untuk memanipulasi DOM) : jQuery adalah Monad
The jQuery dokumentasi sendiri tidak merujuk pada istilah "monad" tapi berbicara tentang "pola pembangun" yang mungkin lebih akrab. Ini tidak mengubah fakta bahwa Anda memiliki monad yang tepat di sana mungkin bahkan tanpa menyadarinya.
sumber
Monad Bukan Metafora , tetapi abstraksi praktis yang berguna muncul dari pola umum, seperti dijelaskan Daniel Spiewak.
sumber
Monad adalah cara menggabungkan komputasi bersama yang berbagi konteks umum. Itu seperti membangun jaringan pipa. Saat membangun jaringan, tidak ada data yang mengalir melaluinya. Tetapi ketika saya telah selesai mengumpulkan semua bit bersama dengan 'bind' dan 'return' maka saya memanggil sesuatu seperti
runMyMonad monad data
dan data mengalir melalui pipa.sumber
Dalam praktiknya, monad adalah implementasi kustom dari operator komposisi fungsi yang menangani efek samping dan nilai input dan return yang tidak kompatibel (untuk rantai).
sumber
Jika saya mengerti dengan benar, IEnumerable diturunkan dari monads. Saya bertanya-tanya apakah itu mungkin sudut pendekatan yang menarik bagi kita dari dunia C #?
Untuk apa nilainya, berikut adalah beberapa tautan ke tutorial yang membantu saya (dan tidak, saya masih belum mengerti apa itu monad).
sumber
Dua hal yang paling membantu saya ketika belajar tentang itu adalah:
Bab 8, "Parsing Fungsional," dari buku Graham Hutton Programming in Haskell . Ini tidak menyebutkan monad sama sekali, sebenarnya, tetapi jika Anda dapat membaca bab dan benar-benar memahami segalanya di dalamnya, terutama bagaimana urutan operasi pengikatan dievaluasi, Anda akan memahami internal monad. Harapkan ini untuk mengambil beberapa percobaan.
Tutorial Semua Tentang Monads . Ini memberikan beberapa contoh bagus penggunaannya, dan saya harus mengatakan bahwa analogi dalam Lampiran saya bekerja untuk saya.
sumber
Monoid tampaknya menjadi sesuatu yang memastikan bahwa semua operasi yang ditentukan pada Monoid dan tipe yang didukung akan selalu mengembalikan tipe yang didukung di dalam Monoid. Misalnya, Semua nomor + Semua nomor = Sejumlah, tidak ada kesalahan.
Sedangkan pembagian menerima dua pecahan, dan mengembalikan pecahan, yang mendefinisikan pembagian dengan nol sebagai Infinity dalam beberapa haskell (yang kebetulan merupakan pecahan pecahan) ...
Bagaimanapun, tampaknya Monads hanyalah cara untuk memastikan bahwa rangkaian operasi Anda berperilaku dengan cara yang dapat diprediksi, dan fungsi yang mengklaim sebagai Num -> Num, yang disusun dengan fungsi lain dari Num-> Num yang dipanggil dengan x tidak katakanlah, tembak rudal.
Di sisi lain, jika kita memiliki fungsi yang menembakkan rudal, kita dapat menyusunnya dengan fungsi lain yang juga menembakkan rudal, karena maksud kita jelas - kita ingin menembakkan rudal - tetapi tidak akan mencoba mencetak "Hello World" untuk beberapa alasan aneh.
Dalam Haskell, utama adalah tipe IO (), atau IO [()], distensinya aneh dan saya tidak akan membahasnya tetapi inilah yang saya pikir terjadi:
Jika saya punya main, saya ingin melakukan serangkaian tindakan, alasan saya menjalankan program adalah untuk menghasilkan efek - biasanya melalui IO. Dengan demikian saya dapat rantai operasi IO bersama-sama di utama untuk melakukan IO, tidak ada yang lain.
Jika saya mencoba melakukan sesuatu yang tidak "mengembalikan IO", program akan mengeluh bahwa rantai tidak mengalir, atau pada dasarnya "Bagaimana ini berhubungan dengan apa yang kita coba lakukan - tindakan IO", tampaknya memaksa programmer untuk terus melatih pemikiran mereka, tanpa menyimpang dan berpikir tentang menembakkan rudal, sambil membuat algoritma untuk menyortir - yang tidak mengalir.
Pada dasarnya, Monads tampaknya menjadi tip bagi kompiler bahwa "hei, Anda tahu fungsi ini yang mengembalikan angka di sini, itu sebenarnya tidak selalu berfungsi, kadang-kadang dapat menghasilkan Angka, dan kadang-kadang Tidak ada sama sekali, simpan saja ini di pikiran". Mengetahui hal ini, jika Anda mencoba untuk menegaskan tindakan monadik, tindakan monadik dapat bertindak sebagai pengecualian waktu kompilasi yang mengatakan "hei, ini sebenarnya bukan angka, ini BISA angka, tetapi Anda tidak dapat menganggap ini, lakukan sesuatu untuk memastikan bahwa alirannya dapat diterima. " yang mencegah perilaku program yang tidak terduga - sampai batas tertentu.
Tampaknya monad bukan tentang kemurnian, atau kontrol, tetapi tentang mempertahankan identitas kategori di mana semua perilaku dapat diprediksi dan didefinisikan, atau tidak dikompilasi. Anda tidak dapat melakukan apa-apa ketika Anda diharapkan untuk melakukan sesuatu, dan Anda tidak dapat melakukan sesuatu jika Anda diharapkan untuk tidak melakukan apa-apa (terlihat).
Alasan terbesar yang dapat saya pikirkan untuk Monads adalah - lihat kode Prosedural / OOP, dan Anda akan melihat bahwa Anda tidak tahu di mana program dimulai, atau berakhir, yang Anda lihat adalah banyak lompatan dan banyak matematika , sihir, dan rudal. Anda tidak akan dapat memeliharanya, dan jika Anda bisa, Anda akan menghabiskan cukup banyak waktu untuk menyelami seluruh program sebelum Anda dapat memahami bagian mana pun darinya, karena modularitas dalam konteks ini didasarkan pada "bagian" yang saling tergantung. kode, di mana kode dioptimalkan untuk menjadi terkait mungkin untuk janji efisiensi / antar-hubungan. Monad sangat konkret, dan didefinisikan dengan baik oleh definisi, dan memastikan bahwa aliran program dimungkinkan untuk dianalisis, dan mengisolasi bagian-bagian yang sulit dianalisis - karena mereka sendiri adalah monad. Monad tampaknya menjadi " atau menghancurkan alam semesta atau bahkan mengubah waktu - kita tidak tahu atau tidak memiliki jaminan bahwa ITU ADALAH ITU. A monad MENJAMIN BAHWA ITU ADALAH APA ITU. yang sangat kuat. atau menghancurkan alam semesta atau bahkan mengubah waktu - kita tidak tahu atau tidak memiliki jaminan bahwa ITU ADALAH ITU. A monad MENJAMIN BAHWA ITU ADALAH APA ITU. yang sangat kuat.
Semua hal di "dunia nyata" tampaknya adalah monad, dalam arti bahwa ia terikat oleh hukum yang dapat diobservasi secara pasti yang mencegah kebingungan. Ini tidak berarti kita harus meniru semua operasi objek ini untuk membuat kelas, sebaliknya kita dapat dengan mudah mengatakan "kotak adalah kotak", hanya kotak, bahkan persegi panjang atau lingkaran, dan "persegi memiliki luas dari panjang salah satu dimensi yang ada dikalikan dengan sendirinya. Tidak peduli apa persegi yang Anda miliki, jika itu persegi dalam ruang 2D, luasnya sama sekali tidak bisa apa-apa selain panjangnya kuadrat, hampir sepele untuk dibuktikan. Ini sangat kuat karena kita tidak perlu membuat pernyataan untuk memastikan bahwa dunia kita sebagaimana adanya, kita hanya menggunakan implikasi realitas untuk mencegah program-program kita dari jalur yang salah.
Saya cukup dijamin salah, tapi saya pikir ini bisa membantu seseorang di luar sana, jadi semoga membantu seseorang.
sumber
Dalam konteks Scala, Anda akan menemukan yang berikut ini sebagai definisi paling sederhana. Pada dasarnya flatMap (atau bind) adalah 'asosiatif' dan ada identitas.
Misalnya
CATATAN Secara tegas definisi Monad dalam pemrograman fungsional tidak sama dengan definisi Monad dalam Kategori Teori , yang didefinisikan secara bergantian
map
danflatten
. Meskipun mereka setara di bawah pemetaan tertentu. Presentasi ini sangat bagus: http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-categorysumber
Jawaban ini dimulai dengan contoh yang memotivasi, bekerja melalui contoh itu, mendapatkan contoh monad, dan secara resmi mendefinisikan "monad".
Pertimbangkan ketiga fungsi ini dalam pseudocode:
f
mengambil pasangan yang dipesan dari formulir<x, messages>
dan mengembalikan pasangan yang dipesan. Ini membuat item pertama tidak tersentuh dan ditambahkan"called f. "
ke item kedua. Sama dengang
.Anda dapat menyusun fungsi-fungsi ini dan mendapatkan nilai asli Anda, bersama dengan string yang menunjukkan urutan fungsi dipanggil:
Anda tidak menyukai kenyataan itu
f
dang
bertanggung jawab untuk menambahkan pesan log mereka sendiri ke informasi logging sebelumnya. (Bayangkan saja demi argumen bahwa alih-alih menambahkan string,f
dang
harus melakukan logika rumit pada item kedua dari pasangan. Akan merepotkan untuk mengulangi logika rumit itu dalam dua - atau lebih - fungsi yang berbeda.)Anda lebih suka menulis fungsi yang lebih sederhana:
Tetapi lihat apa yang terjadi ketika Anda menyusunnya:
Masalahnya adalah bahwa melewatkan pasangan ke suatu fungsi tidak memberi Anda apa yang Anda inginkan. Tetapi bagaimana jika Anda bisa memberi makan pasangan ke suatu fungsi:
Baca
feed(f, m)
sebagai "umpanm
kef
". Untuk pakan pasangan<x, messages>
ke dalam fungsif
adalah untuk lulusx
dalamf
, dapatkan<y, message>
keluar darif
, dan kembali<y, messages message>
.Perhatikan apa yang terjadi ketika Anda melakukan tiga hal dengan fungsi Anda:
Pertama: jika Anda membungkus nilai dan kemudian memberi makan pasangan yang dihasilkan ke fungsi:
Itu sama dengan melewatkan nilai ke fungsi.
Kedua: jika Anda memasukkan pasangan ke
wrap
:Itu tidak mengubah pasangan.
Ketiga: jika Anda mendefinisikan fungsi yang mengambil
x
dang(x)
memasukkanf
:dan beri makan sepasang ke dalamnya:
Itu sama dengan memberi makan pasangan ke
g
dan memberi makan pasangan yang dihasilkan ke dalamf
.Anda memiliki sebagian besar monad. Sekarang Anda hanya perlu tahu tentang tipe data dalam program Anda.
Apa jenis nilai itu
<x, "called f. ">
? Yah, itu tergantung pada jenis nilaix
apa. Jikax
tipet
, maka pasangan Anda adalah nilai tipe "pair oft
and string". Panggil tipe ituM t
.M
adalah konstruktor tipe:M
sendirian tidak merujuk ke tipe, tetapiM _
merujuk ke tipe setelah Anda mengisi kosong dengan tipe. AnM int
adalah sepasang int dan string. AnM string
adalah sepasang string dan string. DllSelamat, Anda telah membuat monad!
Secara formal, monad Anda adalah tuple
<M, feed, wrap>
.Monad adalah tuple di
<M, feed, wrap>
mana:M
adalah tipe konstruktor.feed
mengambil (fungsi yang mengambil at
dan mengembalikan sebuahM u
) dan anM t
dan mengembalikan sebuahM u
.wrap
mengambilv
dan mengembalikan sebuahM v
.t
,,u
danv
tiga jenis yang mungkin sama atau tidak sama. Monad memenuhi tiga properti yang Anda buktikan untuk monad spesifik Anda:Memberi makan yang dibungkus
t
dengan suatu fungsi sama dengan melewatkan yang tidak dibungkust
ke dalam fungsi.Secara formal:
feed(f, wrap(x)) = f(x)
Memberi makan
M t
ke dalamwrap
tidak ada hubungannya denganM t
.Secara formal:
feed(wrap, m) = m
Mengumpankan
M t
(sebut sajam
) ke fungsi itut
ke dalamg
M u
(sebut sajan
) darig
n
kef
sama dengan
m
keg
n
darig
n
kef
Secara formal: di
feed(h, m) = feed(f, feed(g, m))
manah(x) := feed(f, g(x))
Biasanya,
feed
disebutbind
(AKA>>=
di Haskell) danwrap
disebutreturn
.sumber
Saya akan mencoba menjelaskan
Monad
dalam konteks Haskell.Dalam pemrograman fungsional, komposisi fungsi penting. Ini memungkinkan program kami terdiri dari fungsi kecil dan mudah dibaca.
Katakanlah kita memiliki dua fungsi:
g :: Int -> String
danf :: String -> Bool
.Kita dapat melakukan
(f . g) x
, yang sama denganf (g x)
, di manax
adalahInt
nilai.Ketika melakukan komposisi / menerapkan hasil dari satu fungsi ke fungsi lain, memiliki jenis yang cocok adalah penting. Dalam kasus di atas, jenis hasil yang dikembalikan oleh
g
harus sama dengan jenis yang diterima olehf
.Tetapi kadang-kadang nilai berada dalam konteks, dan ini membuatnya sedikit lebih mudah untuk berbaris jenis. (Memiliki nilai dalam konteks sangat berguna. Misalnya,
Maybe Int
tipe tersebut merepresentasikanInt
nilai yang mungkin tidak ada,IO String
tipe tersebut mewakiliString
nilai yang ada sebagai hasil dari melakukan beberapa efek samping.)Katakanlah sekarang kita punya
g1 :: Int -> Maybe String
danf1 :: String -> Maybe Bool
.g1
danf1
sangat mirip dengang
danf
masing masing.Kita tidak bisa melakukan
(f1 . g1) x
atauf1 (g1 x)
, di manax
merupakanInt
nilai. Jenis hasil yang dikembalikang1
bukan apaf1
diharapkan.Kita bisa menulis
f
dang
dengan.
operator, tetapi sekarang kita tidak bisa menulisf1
dang1
dengan.
. Masalahnya adalah bahwa kita tidak bisa langsung meneruskan nilai dalam konteks ke fungsi yang mengharapkan nilai yang tidak ada dalam konteks.Bukankah lebih baik jika kita memperkenalkan operator untuk menulis
g1
danf1
, sehingga kita bisa menulis(f1 OPERATOR g1) x
?g1
mengembalikan nilai dalam konteks. Nilai akan diambil di luar konteks dan diterapkan kef1
. Dan ya, kami memiliki operator seperti itu. Nya<=<
.Kami juga punya
>>=
operator yang melakukan hal yang sama persis bagi kami, meskipun dalam sintaks yang sedikit berbeda.Kita menulis:
g1 x >>= f1
.g1 x
adalah sebuahMaybe Int
nilai. The>>=
Operator membantu mengambilInt
nilai dari "mungkin-tidak-ada" konteks, dan menerapkannya kef1
. Hasilf1
, yang merupakanMaybe Bool
, akan menjadi hasil dari seluruh>>=
operasi.Dan akhirnya, mengapa
Monad
bermanfaat? KarenaMonad
adalah tipe kelas yang mendefinisikan>>=
operator, sangat mirip denganEq
kelas tipe yang mendefinisikan==
dan/=
operator.Untuk menyimpulkan,
Monad
kelas tipe mendefinisikan>>=
operator yang memungkinkan kami untuk melewatkan nilai dalam konteks (kami menyebutnya nilai monadik) ke fungsi yang tidak mengharapkan nilai dalam konteks. Konteks akan dijaga.Jika ada satu hal yang perlu diingat di sini, itu
Monad
memungkinkan komposisi fungsi yang melibatkan nilai-nilai dalam konteks .sumber
tl; dr
Prolog
Operator aplikasi
$
fungsididefinisikan secara kanonik
dalam hal aplikasi fungsi Haskell-primitif
f x
(infixl 10
).Komposisi
.
didefinisikan$
sebagaidan memenuhi persamaan
forall f g h.
.
bersifat asosiatif, danid
merupakan identitas kanan dan kirinya.Triple Kleisli
Dalam pemrograman, monad adalah konstruktor tipe functor dengan turunan dari kelas tipe monad. Ada beberapa varian definisi dan implementasi yang setara, masing-masing membawa intuisi yang sedikit berbeda tentang abstraksi monad.
Sebuah functor adalah konstruktor
f
tipe* -> *
dengan instance kelas tipe functor.Selain mengikuti protokol tipe yang ditegakkan secara statis, instance dari kelas tipe functor harus mematuhi hukum functor aljabar
forall f g.
Functor perhitungan memiliki jenis
Suatu perhitungan
c r
terdiri dari hasilr
dalam konteksc
.Fungsi monadik unary atau panah Kleisli memiliki tipe
Panah Kleisi adalah fungsi yang mengambil satu argumen
a
dan mengembalikan perhitungan monadikm b
.Monad secara kanonik didefinisikan dalam istilah triple Kleisli
forall m. Functor m =>
diimplementasikan sebagai kelas tipe
The Kleisli identitas
return
adalah panah Kleisli yang mempromosikan nilait
dalam konteks monadikm
. Aplikasi ekstensi atau Kleisli=<<
menerapkan panah Kleislia -> m b
ke hasil perhitunganm a
.Komposisi Kleisli
<=<
didefinisikan dalam hal ekstensi sebagai<=<
menyusun dua panah Kleisli, menerapkan panah kiri ke hasil aplikasi panah kanan.Contoh dari kelas tipe monad harus mematuhi hukum monad , yang paling elegan dinyatakan dalam komposisi Kleisli:
forall f g h.
<=<
bersifat asosiatif, danreturn
merupakan identitas kanan dan kirinya.Identitas
Tipe identitas
adalah fungsi identitas pada tipe
Ditafsirkan sebagai functor,
Dalam Haskell kanonik, identitas monad didefinisikan
Pilihan
Jenis opsi
mengkodekan perhitungan
Maybe t
yang tidak selalu menghasilkan hasilt
, perhitungan yang mungkin "gagal". Opsi monad ditentukana -> Maybe b
diterapkan pada hasil hanya jikaMaybe a
menghasilkan suatu hasil.Bilangan alami dapat dikodekan sebagai bilangan bulat yang lebih besar dari atau sama dengan nol.
Bilangan alami tidak ditutup dengan pengurangan.
Opsi monad mencakup bentuk dasar penanganan pengecualian.
Daftar
Daftar monad, lebih dari tipe daftar
dan operasi tambahan “monoid” tambahannya
mengkodekan perhitungan nonlinier
[t]
menghasilkan jumlah0, 1, ...
hasil yang alamit
.Ekstensi
=<<
menggabungkan++
semua daftar yang[b]
dihasilkan dari aplikasif x
panah Kleislia -> [b]
hingga elemen[a]
ke dalam daftar hasil tunggal[b]
.Biarkan pembagi yang tepat dari bilangan bulat positif
n
menjadikemudian
Dalam mendefinisikan kelas tipe monad, alih-alih ekstensi
=<<
, standar Haskell menggunakan flip, operator bind>>=
.Demi kesederhanaan, penjelasan ini menggunakan hirarki kelas tipe
Di Haskell, hirarki standar saat ini adalah
karena tidak hanya setiap monad adalah functor, tetapi setiap aplikasi adalah functor dan setiap monad adalah aplikasi juga.
Menggunakan daftar monad, kodesemu imperatif
diterjemahkan secara kasar ke blok do ,
pemahaman setara monad ,
dan ekspresinya
Do notasi dan pemahaman monad adalah gula sintaksis untuk ekspresi ikatan terikat. Operator mengikat digunakan untuk pengikatan nama lokal dari hasil monadik.
dimana
Fungsi penjaga didefinisikan
di mana jenis unit atau "tuple kosong"
Monad tambahan yang mendukung pilihan dan kegagalan dapat diabstraksi menggunakan kelas tipe
di mana
fail
dan<|>
membentuk monoidforall k l m.
dan
fail
merupakan elemen nol yang menyerap / memusnahkan monad aditifJika dalam
even p
benar, maka penjaga menghasilkan[()]
, dan, dengan definisi>>
, fungsi konstan lokalditerapkan pada hasilnya
()
. Jika salah, maka penjaga menghasilkan daftar monad'sfail
([]
), yang tidak membuahkan hasil untuk panah Kleisli untuk diterapkan>>
, jadi inip
dilewati.Negara
Secara tidak terkenal, monad digunakan untuk menyandikan perhitungan stateful.
Sebuah prosesor negara adalah fungsi
yang mentransisikan keadaan
st
dan menghasilkan hasilnyat
. The negarast
bisa apa saja. Tidak ada, flag, count, array, handle, machine, world.Jenis prosesor negara biasanya disebut
State processor monad adalah
* -> *
functor yang diketikState st
. Panah Kleisli dari prosesor negara monad adalah fungsiDalam Haskell kanonik, versi malas dari monad prosesor negara didefinisikan
Prosesor status dijalankan dengan memasok kondisi awal:
Akses negara disediakan oleh primitif
get
danput
, metode abstraksi atas stateful monads:m -> st
mendeklarasikan ketergantungan fungsional dari tipe negarast
pada monadm
; bahwaState t
, misalnya, akan menentukan tipe negara untuk menjadit
unik.dengan tipe unit yang digunakan secara analog
void
dalam C.gets
sering digunakan dengan accessors bidang rekaman.Negara setara monad dari variabel threading
di mana
s0 :: Int
, adalah sama-sama transparan referensial, tetapi jauh lebih elegan dan praktismodify (+ 1)
adalah perhitungan tipeState Int ()
, kecuali efeknya setara denganreturn ()
.Hukum asosiatif monad dapat ditulis dalam istilah
>>=
forall m f g.
atau
Seperti dalam pemrograman berorientasi ekspresi (misalnya Rust), pernyataan terakhir dari sebuah blok mewakili hasilnya. Operator mengikat kadang-kadang disebut "titik koma diprogram".
Struktur kontrol iterasi primitif dari pemrograman imperatif terstruktur ditiru secara monadik
Input output
Prosesor negara dunia I / O adalah rekonsiliasi dari Haskell murni dan dunia nyata, dari semantik fungsional denotatif dan imperatif operasional. Sebuah analog dekat dari implementasi ketat yang sebenarnya:
Interaksi difasilitasi oleh primitif yang tidak murni
Pengotor kode yang menggunakan
IO
primitif secara permanen di-protokol-kan oleh sistem tipe. Karena kemurnian luar biasa, apa yang terjadiIO
, tetap masukIO
.Atau, setidaknya, seharusnya.
Jenis tanda tangan dari program Haskell
meluas ke
Fungsi yang mengubah dunia.
Epilog
Kategori objek mana yang merupakan tipe Haskell dan morfisme mana yang merupakan fungsi antara tipe Haskell adalah, “cepat dan longgar”, kategorinya
Hask
.Functor
T
adalah pemetaan dari kategoriC
ke kategoriD
; untuk setiap objek dalamC
suatu objek diD
dan untuk setiap morfisme dalam
C
morfisme diD
di mana
X
,Y
adalah objek diC
.HomC(X, Y)
adalah kelas homomorfisme dari semua morfismeX -> Y
diC
. Functor harus menjaga identitas dan komposisi morfisme, "struktur" dariC
, diD
.The kategori Kleisli dari kategori
C
diberikan oleh Kleisli tigaseorang endofunctor
(
f
), morfisme identitaseta
(return
), dan operator ekstensi*
(=<<
).Setiap morfisme Kleisli di
Hask
oleh operator ekstensi
diberikan morfisme dalam
Hask
kategori KleisliKomposisi dalam kategori Kleisli
.T
diberikan dalam hal ekstensidan memenuhi aksioma kategori
yang, menerapkan transformasi kesetaraan
dalam hal ekstensi diberikan secara kanonik
Monads juga dapat didefinisikan bukan dari ekstensi Kleislian, tetapi transformasi alami
mu
, dalam pemrograman yang disebutjoin
. Monad didefinisikanmu
sebagai triple atas kategoriC
, dari endofunctordan dua tranformasi alami
memenuhi persamaan
Kelas tipe monad kemudian didefinisikan
mu
Implementasi kanon opsi monad:The
concat
fungsiadalah
join
daftar monad.Implementasi
join
dapat diterjemahkan dari formulir ekstensi menggunakan kesetaraanTerjemahan terbalik dari
mu
ke bentuk ekstensi diberikan olehPhilip Wadler: Monads untuk pemrograman fungsional
Simon L Peyton Jones, Philip Wadler: pemrograman fungsional imperatif
Jonathan MD Hill, Keith Clarke: Pengantar teori kategori, teori kategori monad, dan hubungannya dengan pemrograman fungsional ´
Kategori Kleisli
Eugenio Moggi: Pengertian tentang komputasi dan monad
Apa yang bukan monad
dari Generalising Monads ke Arrows oleh John Hughes
sumber
Apa yang dibutuhkan dunia adalah posting blog monad lain, tapi saya pikir ini berguna dalam mengidentifikasi monad yang ada di alam liar.
sumber
http://code.google.com/p/monad-tutorial/ adalah pekerjaan yang sedang berlangsung untuk menjawab pertanyaan ini dengan tepat.
sumber
Biarkan "
{| a |m}
" di bawah ini mewakili sebagian data monadik. Tipe data yang mengiklankana
:Function,,
f
tahu cara membuat monad, jika saja itu memilikia
:Di sini kita melihat fungsinya,,
f
mencoba untuk mengevaluasi sebuah monad tetapi mendapat teguran.Funtion,,
f
menemukan cara untuk mengekstraka
dengan menggunakan>>=
.Sedikit yang
f
tahu, monad dan>>=
berkolusi.Tapi apa yang sebenarnya mereka bicarakan? Yah, itu tergantung monad. Berbicara semata-mata dalam abstrak memiliki penggunaan yang terbatas; Anda harus memiliki pengalaman dengan monad tertentu untuk menyempurnakan pemahaman.
Misalnya, tipe data Maybe
memiliki turunan monad yang akan bertindak seperti berikut ...
Di mana, jika kasusnya
Just a
Tetapi untuk kasus
Nothing
Jadi, Mungkin monad memungkinkan perhitungan dilanjutkan jika benar-benar berisi
a
iklan yang diiklankannya, tetapi batalkan perhitungan jika tidak. Hasilnya, bagaimanapun masih sepotong data monadik, meskipun bukan outputf
. Untuk alasan ini, monad Mungkin digunakan untuk mewakili konteks kegagalan.Monad yang berbeda berperilaku berbeda. Daftar adalah tipe data lain dengan instance monadik. Mereka berperilaku seperti berikut:
Dalam hal ini, fungsi tahu bagaimana membuat daftar dari inputnya, tetapi tidak tahu apa yang harus dilakukan dengan input ekstra dan daftar tambahan. Bind
>>=
, membantuf
dengan menggabungkan beberapa output. Saya menyertakan contoh ini untuk menunjukkan bahwa walaupun>>=
bertanggung jawab untuk mengekstraksia
, ia juga memiliki akses ke keluaran terikat akhirnyaf
. Memang, ia tidak akan pernah mengekstrak apa puna
kecuali ia tahu keluaran akhirnya memiliki jenis konteks yang sama.Ada monad lain yang digunakan untuk mewakili konteks yang berbeda. Berikut beberapa penokohan dari beberapa karakter lainnya. The
IO
monad tidak benar-benar memilikia
, tapi ia tahu seorang pria dan akan mendapatkan bahwaa
untuk Anda. TheState st
monad memiliki simpanan rahasiast
yang akan lolos kef
bawah meja, meskipunf
hanya datang meminta untuka
. TheReader r
monad ini mirip denganState st
, meskipun hanya memungkinkanf
melihatr
.Maksud dari semua ini adalah bahwa semua jenis data yang dinyatakan sebagai Monad menyatakan semacam konteks sekitar penggalian nilai dari monad. Keuntungan besar dari semua ini? Yah, cukup mudah untuk menulis perhitungan dengan semacam konteks. Namun, hal itu bisa berantakan ketika merangkai kalkulasi sarat berbagai konteks. Operasi monad menangani penyelesaian interaksi konteks sehingga programmer tidak perlu melakukannya.
Perhatikan, bahwa penggunaan
>>=
eases berantakan dengan mengambil beberapa otonomi darif
. Artinya, dalam kasus diNothing
atas misalnya,f
tidak lagi bisa memutuskan apa yang harus dilakukan dalam kasusNothing
; itu dikodekan dalam>>=
. Ini adalah trade off. Jika perlu untukf
memutuskan apa yang harus dilakukan dalam kasusNothing
, makaf
seharusnya fungsi dariMaybe a
keMaybe b
. Dalam hal ini,Maybe
menjadi seorang monad tidak relevan.Namun, perlu diketahui bahwa kadang-kadang tipe data tidak mengekspor konstruktornya (melihat Anda IO), dan jika kami ingin bekerja dengan nilai yang diiklankan, kami tidak punya banyak pilihan selain bekerja dengan antarmuka monadik itu.
sumber
Monad adalah benda yang digunakan untuk merangkum objek yang memiliki keadaan berubah. Paling sering dijumpai dalam bahasa-bahasa yang sebaliknya tidak memungkinkan Anda memiliki keadaan yang dapat dimodifikasi (mis., Haskell).
Contohnya adalah untuk file I / O.
Anda akan dapat menggunakan monad untuk file I / O untuk mengisolasi sifat keadaan yang berubah menjadi hanya kode yang menggunakan Monad. Kode di dalam Monad dapat secara efektif mengabaikan perubahan keadaan dunia di luar Monad - ini membuatnya lebih mudah untuk mempertimbangkan efek keseluruhan dari program Anda.
sumber