Pertanyaan Apa itu monad?


Setelah sempat melihat Haskell baru-baru ini, apa yang akan terjadi singkat, ringkas, praktis penjelasan seperti apa monad dasarnya?

Saya telah menemukan sebagian besar penjelasan yang saya temui sebagai tidak dapat diakses dan kurang detail praktis.


1227


asal


Jawaban:


Pertama: Istilah itu monad sedikit hampa jika Anda bukan seorang matematikawan. Istilah alternatif adalah pembangun komputasi yang sedikit lebih deskriptif tentang apa yang sebenarnya berguna bagi mereka.

Anda meminta contoh praktis:

Contoh 1: Pemahaman daftar:

[x*2 | x<-[1..10], odd x]

Ungkapan ini mengembalikan ganda dari semua angka ganjil dalam rentang dari 1 hingga 10. Sangat berguna!

Ternyata ini hanyalah gula sintaksis untuk beberapa operasi dalam daftar monad. Pemahaman daftar yang sama dapat ditulis sebagai:

do
   x <- [1..10]
   if odd x 
       then [x * 2] 
       else []

Atau bahkan:

[1..10] >>= (\x -> if odd x then [x*2] else [])

Contoh 2: Input / Output:

do
   putStrLn "What is your name?"
   name <- getLine
   putStrLn ("Welcome, " ++ name ++ "!")

Kedua contoh menggunakan monads, pembangun komputasi AKA. Tema umumnya adalah monad itu operasi rantai dengan cara tertentu yang bermanfaat. Dalam pemahaman daftar, operasi dirantai sedemikian rupa sehingga jika suatu operasi mengembalikan daftar, maka operasi berikut dilakukan setiap barang dalam daftar. IO monad di sisi lain melakukan operasi secara berurutan, tetapi melewati "variabel tersembunyi", yang mewakili "keadaan dunia", yang memungkinkan kita menulis kode I / O dengan cara yang murni fungsional.

Ternyata pola operasi chaining cukup berguna dan digunakan untuk banyak hal yang berbeda di Haskell.

Contoh lain adalah pengecualian: Menggunakan Error monad, operasi dirantai sedemikian rupa sehingga mereka dilakukan secara berurutan, kecuali jika kesalahan dilempar, dalam hal ini sisa rantai ditinggalkan.

Kedua sintaks daftar-pemahaman dan notasi adalah gula sintaksis untuk operasi chaining menggunakan >>= operator. Monad pada dasarnya hanyalah tipe yang mendukung >>= operator.

Contoh 3: Pengurai

Ini adalah parser yang sangat sederhana yang mem-parsing string atau nomor yang dikutip:

parseExpr = parseString <|> parseNumber

parseString = do
        char '"'
        x <- many (noneOf "\"")
        char '"'
        return (StringValue x)

parseNumber = do
    num <- many1 digit
    return (NumberValue (read num))

Operasinya char, digit, dll. sangat sederhana. Mereka cocok atau tidak cocok. Sihir adalah monad yang mengatur aliran kontrol: Operasi dilakukan secara berurutan sampai pertandingan gagal, dalam hal ini monad mundur ke yang terbaru <|> dan mencoba opsi selanjutnya. Sekali lagi, cara operasi chaining dengan beberapa semantik tambahan yang bermanfaat.

Contoh 4: Pemrograman asinkron

Contoh-contoh di atas ada di Haskell, tetapi ternyata F # juga mendukung monads. Contoh ini dicuri dari Don Syme:

let AsyncHttp(url:string) =
    async {  let req = WebRequest.Create(url)
             let! rsp = req.GetResponseAsync()
             use stream = rsp.GetResponseStream()
             use reader = new System.IO.StreamReader(stream)
             return reader.ReadToEnd() }

Metode ini menjemput halaman web. Garis pukulan adalah penggunaan GetResponseAsync - sebenarnya menunggu respons pada utas yang terpisah, sementara utas utama kembali dari fungsi. Tiga baris terakhir dieksekusi pada thread yang di-spawn ketika respon telah diterima.

Di sebagian besar bahasa lain, Anda harus secara eksplisit membuat fungsi terpisah untuk garis yang menangani respons. Itu async monad mampu "membagi" blok itu sendiri dan menunda pelaksanaan paruh kedua. (Itu async {} sintaks menunjukkan bahwa aliran kontrol di blok didefinisikan oleh async monad.)

Bagaimana cara kerjanya

Jadi bagaimana monad bisa melakukan semua hal kontrol aliran yang mewah ini? Apa yang sebenarnya terjadi dalam do-block (atau a perhitungan komputasi seperti yang disebut dalam F #), adalah bahwa setiap operasi (pada dasarnya setiap baris) dibungkus dalam fungsi anonim terpisah. Fungsi-fungsi ini kemudian digabungkan dengan menggunakan bind operator (dieja >>= di Haskell). Sejak itu bind operasi menggabungkan fungsi, itu dapat mengeksekusi mereka karena melihat cocok: secara berurutan, beberapa kali, secara terbalik, membuang beberapa, mengeksekusi beberapa pada utas yang terpisah ketika terasa seperti itu dan seterusnya.

Sebagai contoh, ini adalah versi IO-code yang diperluas dari contoh 2:

putStrLn "What is your name?"
>>= (\_ -> getLine)
>>= (\name -> putStrLn ("Welcome, " ++ name ++ "!"))

Ini lebih buruk, tetapi juga lebih jelas apa yang sebenarnya terjadi. Itu >>= operator adalah bahan ajaib: Dibutuhkan nilai (di sisi kiri) dan menggabungkannya dengan fungsi (di sisi kanan), untuk menghasilkan nilai baru. Nilai baru ini kemudian diambil oleh yang berikutnya >>= operator dan lagi dikombinasikan dengan fungsi untuk menghasilkan nilai baru. >>= dapat dilihat sebagai evaluator mini.

Perhatikan itu >>= kelebihan beban untuk berbagai jenis, sehingga setiap monad memiliki implementasi sendiri >>=. (Semua operasi dalam rantai harus menjadi tipe monad yang sama, jika tidak, the >>= operator tidak akan berfungsi.)

Penerapan paling sederhana dari >>= hanya mengambil nilai di sebelah kiri dan menerapkannya ke 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 penerapan monad >>=.

Ada beberapa kecerdikan tambahan tentang bagaimana nilai-nilai dilewatkan dari satu operasi ke operasi berikutnya, tetapi ini membutuhkan penjelasan yang lebih dalam dari sistem tipe Haskell.

Menyimpulkan

Dalam Haskell-terms monad adalah tipe parameter yang merupakan turunan dari kelas tipe Monad, yang mendefinisikan >>= bersama dengan beberapa operator lain. Dalam istilah awam, monad hanyalah tipe untuk yang >>= operasi didefinisikan.

Sendiri >>= hanyalah cara yang tidak praktis untuk fungsi-fungsi chaining, tetapi dengan kehadiran notasi yang menyembunyikan "plumbing", operasi monadik ternyata merupakan abstraksi yang sangat bagus dan berguna, banyak tempat yang berguna dalam bahasa, dan berguna untuk menciptakan bahasa mini Anda sendiri dalam bahasa.

Mengapa monads sulit?

Bagi banyak Haskell-learners, monads adalah hambatan yang mereka pukul seperti dinding bata. Bukan berarti monad itu sendiri rumit, tetapi implementasinya bergantung pada banyak fitur Haskell tingkat lanjut lainnya seperti tipe parameter, kelas tipe, dan sebagainya. Masalahnya adalah bahwa Haskell I / O didasarkan pada monads, dan I / O mungkin adalah salah satu hal pertama yang ingin Anda pahami ketika mempelajari bahasa baru - bagaimanapun, tidak terlalu menyenangkan untuk membuat program yang tidak menghasilkan apa pun. keluaran. Saya tidak punya solusi segera untuk masalah ayam dan telur ini, kecuali memperlakukan I / O seperti "keajaiban terjadi di sini" sampai Anda memiliki cukup pengalaman dengan bagian bahasa lain. Maaf.

Blog yang sangat bagus di monads: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html


965



Menjelaskan "apa itu monad" sedikit seperti mengatakan "apa itu angka?" Kami menggunakan angka sepanjang waktu. Tetapi bayangkan Anda bertemu seseorang yang tidak tahu apa-apa tentang angka. Bagaimana heck maukah kamu menjelaskan nomor apa? Dan bagaimana Anda bahkan mulai menjelaskan mengapa itu mungkin berguna?

Apa itu monad? Jawaban singkat: Ini adalah cara khusus untuk operasi chaining bersama.

Intinya, Anda menulis langkah-langkah eksekusi dan menghubungkannya dengan "fungsi mengikat". (Di Haskell, namanya >>=.) Anda dapat menulis panggilan ke operator bind sendiri, atau Anda dapat menggunakan gula sintaks yang membuat compiler memasukkan panggilan fungsi tersebut untuk Anda. Namun demikian, setiap langkah dipisahkan oleh panggilan ke fungsi bind ini.

Jadi fungsi mengikat 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 keras, bukan? Tetapi ada lebih dari satu semacam monad. Mengapa? Bagaimana?

Nah, fungsi yang mengikat bisa ambil saja hasilnya dari satu langkah, dan berikan ke langkah berikutnya. Tetapi jika itu "semua" monad tidak ... itu sebenarnya tidak terlalu berguna. Dan itu penting untuk dipahami: Setiap berguna monad melakukan sesuatu yang lain sebagai tambahan hanya menjadi monad. Setiap berguna monad memiliki "kekuatan khusus", yang membuatnya unik.

(A monad yang dilakukannya tidak ada khusus disebut "monad identitas". Agak seperti fungsi identitas, ini terdengar seperti hal yang sama sekali tidak berguna, namun ternyata tidak ... Tapi itu cerita lain ™.)

Pada dasarnya, masing-masing monad memiliki implementasi fungsi mengikatnya sendiri. Dan Anda dapat menulis fungsi yang mengikat sedemikian rupa sehingga hal-hal tersebut terhapus di antara langkah-langkah eksekusi. Sebagai contoh:

  • Jika setiap langkah mengembalikan indikator keberhasilan / kegagalan, Anda dapat mengikat langkah selanjutnya hanya jika yang sebelumnya berhasil. Dengan cara ini, langkah gagal membatalkan seluruh urutan "secara otomatis", tanpa pengujian bersyarat dari Anda. (Itu Kegagalan Monad.)

  • Memperluas gagasan ini, Anda dapat menerapkan "pengecualian". (Itu Galat Monad atau Exception Monad.) Karena Anda mendefinisikannya sendiri daripada menjadi fitur bahasa, Anda dapat menentukan cara kerjanya. (Mis., Mungkin Anda ingin mengabaikan dua pengecualian pertama dan hanya membatalkan saat a ketiga pengecualian dibuang.)

  • Anda dapat membuat setiap langkah kembali beberapa hasil, dan memiliki fungsi mengikat loop di atasnya, memberi makan masing-masing ke langkah berikutnya untuk Anda. Dengan cara ini, Anda tidak perlu terus menulis di seluruh tempat ketika berhadapan dengan banyak hasil. Fungsi mengikat "otomatis" melakukan semua itu untuk Anda. (Itu Daftar Monad.)

  • Serta melewati "hasil" dari satu langkah ke langkah lainnya, Anda dapat memiliki fungsi mengikat berikan data tambahan sekitar juga. Data ini sekarang tidak muncul di kode sumber Anda, tetapi Anda masih dapat mengaksesnya dari mana saja, tanpa harus secara manual meneruskannya ke setiap fungsi. (Itu Pembaca Monad.)

  • Anda dapat membuatnya sehingga "data ekstra" dapat diganti. Ini memungkinkan Anda untuk melakukannya mensimulasikan pembaruan yang merusak, tanpa benar-benar melakukan pembaruan yang merusak. (Itu State Monad dan sepupunya Penulis Monad.)

  • Karena kamu hanya simulasi Pembaruan yang merusak, Anda dapat dengan mudah melakukan hal-hal yang tidak mungkin dilakukan nyata pembaruan yang merusak. Misalnya, Anda bisa batalkan pembaruan terakhir, atau kembali ke versi yang lebih lama.

  • Anda dapat membuat monad di mana perhitungan dapat dilakukan berhenti, sehingga Anda dapat menghentikan program Anda, masuk dan mengotak-atik data keadaan internal, dan kemudian melanjutkannya.

  • Anda dapat menerapkan "kelanjutan" sebagai monad. Ini memungkinkan Anda untuk melakukannya menghancurkan pikiran orang!

Semua ini dan banyak lagi mungkin dengan monads. Tentu saja, semua ini juga sangat mungkin tanpa monads juga. Ini sangat drastis lebih mudah menggunakan monads.


638



Sebenarnya, bertentangan dengan pemahaman umum tentang Monads, mereka tidak ada hubungannya dengan negara. Monads hanyalah cara untuk membungkus sesuatu dan menyediakan metode untuk melakukan operasi pada barang yang dibungkus tanpa membukanya.

Misalnya, Anda dapat membuat jenis untuk membungkus yang lain, di Haskell:

data Wrapped a = Wrap a

Untuk membungkus hal-hal yang kita definisikan

return :: a -> Wrapped a
return x = Wrap x

Untuk melakukan operasi tanpa membuka bungkus, katakanlah Anda memiliki fungsi f :: a -> b, maka Anda bisa melakukan ini mengangkat fungsi itu untuk bertindak berdasarkan nilai yang dibungkus:

fmap :: (a -> b) -> (Wrapped a -> Wrapped b)
fmap f (Wrap x) = Wrap (f x)

Itu semua yang perlu dipahami. Namun, ternyata ada fungsi yang lebih umum untuk melakukan hal ini pengangkatan, yang mana bind:

bind :: (a -> Wrapped b) -> (Wrapped a -> Wrapped b)
bind f (Wrap x) = f x

bind dapat melakukan sedikit lebih dari fmap, tapi tidak sebaliknya. Sebenarnya, fmap dapat didefinisikan hanya dalam bentuk bind dan return. Jadi, ketika mendefinisikan monad .. Anda memberikan tipenya (ini dia Wrapped a) dan kemudian mengatakan bagaimana itu return dan bind operasi bekerja.

Yang asyik adalah ini ternyata menjadi pola umum yang muncul di seluruh tempat, mengenkapsulasi negara dengan cara murni hanyalah salah satunya.

Untuk artikel yang bagus tentang bagaimana monads dapat digunakan untuk memperkenalkan dependensi fungsional dan dengan demikian mengontrol urutan evaluasi, seperti yang digunakan dalam IO monad milik Haskell, periksa IO Inside.

Untuk memahami monads, jangan terlalu khawatir tentang itu. Baca tentang mereka apa yang Anda anggap menarik dan jangan khawatir jika Anda tidak langsung mengerti. Maka hanya menyelam dalam bahasa seperti Haskell adalah cara untuk pergi. Monads adalah salah satu dari hal-hal di mana pemahaman menetes ke otak Anda dengan latihan, suatu hari Anda baru saja tiba-tiba menyadari bahwa Anda memahaminya.


164



Tapi, Anda mungkin telah menemukan Monads!

sigfpe mengatakan:

Tetapi semua ini memperkenalkan monad sebagai sesuatu yang esoteris yang membutuhkan penjelasan. Tapi yang ingin saya sampaikan adalah bahwa mereka tidak esoterik sama sekali. Bahkan, dihadapkan dengan berbagai masalah dalam pemrograman fungsional, Anda akan dituntun, tak terelakkan, untuk solusi tertentu, yang semuanya merupakan contoh dari monads. Bahkan, saya berharap Anda dapat menemukan mereka sekarang jika Anda belum melakukannya. Ini adalah langkah kecil untuk memperhatikan bahwa semua solusi ini sebenarnya adalah solusi yang sama dalam penyamaran. Dan setelah membaca ini, Anda mungkin berada dalam posisi yang lebih baik untuk memahami dokumen lain di monads karena Anda akan mengenali semua yang Anda lihat sebagai sesuatu yang sudah Anda temukan.

Banyak masalah yang berusaha diselesaikan oleh monad terkait dengan masalah efek samping. Jadi kita akan mulai dengan mereka. (Perhatikan bahwa monads memungkinkan Anda melakukan lebih dari sekadar menangani efek samping, khususnya banyak jenis objek kontainer dapat dilihat sebagai monads. Beberapa perkenalan ke monads merasa sulit untuk merekonsiliasi dua penggunaan monads yang berbeda ini dan berkonsentrasi hanya pada satu atau lebih yang lain.)

Dalam bahasa pemrograman imperatif seperti C ++, fungsi tidak berperilaku seperti fungsi matematika. Sebagai contoh, misalkan kita memiliki fungsi C ++ yang mengambil argumen floating point tunggal dan mengembalikan hasil floating point. Secara dangkal mungkin tampak sedikit seperti referensi fungsi matematika reals ke real, tetapi fungsi C + + dapat melakukan lebih dari sekedar mengembalikan angka yang tergantung pada argumennya. Dapat membaca dan menulis nilai-nilai variabel global serta menulis output ke layar dan menerima masukan dari pengguna. Dalam bahasa fungsional murni, bagaimanapun, suatu fungsi hanya dapat membaca apa yang dipasok ke dalam argumennya dan satu-satunya cara itu dapat memiliki efek pada dunia adalah melalui nilai-nilai itu kembali.


161



Monad adalah datatype yang memiliki dua operasi: >>= (aka bind) dan return (aka unit). return mengambil nilai sewenang-wenang dan membuat instance dari monad dengannya. >>= mengambil contoh monad dan memetakan fungsi di atasnya. (Anda dapat melihat sudah bahwa monad adalah jenis datatype yang aneh, karena di sebagian besar bahasa pemrograman, Anda tidak dapat menulis fungsi yang mengambil nilai acak dan membuat jenis darinya. Monads menggunakan sejenis polimorfisme parametrik.)

Dalam notasi Haskell, antarmuka monad ditulis

class Monad m where
  return :: a -> m a
  (>>=) :: forall a b . m a -> (a -> m b) -> m b

Operasi-operasi ini seharusnya mematuhi "hukum" tertentu, tetapi itu tidak sangat penting: "hukum" hanya mengkodifikasi cara penerapan yang masuk akal dari operasi harus berperilaku (pada dasarnya, bahwa >>= dan return harus setuju tentang bagaimana nilai dapat diubah menjadi contoh monad dan itu >>= bersifat asosiatif).

Monad bukan hanya tentang negara dan I / O: mereka abstrak pola umum komputasi yang termasuk bekerja dengan negara, I / O, pengecualian, dan non-determinisme. Mungkin monads yang paling sederhana untuk dipahami adalah daftar dan jenis opsi:

instance Monad [ ] where
    []     >>= k = []
    (x:xs) >>= k = k x ++ (xs >>= k)
    return x     = [x]

instance Monad Maybe where
    Just x  >>= k = k x
    Nothing >>= k = Nothing
    return x      = Just x

dimana [] dan : adalah daftar konstruktor, ++adalah operator gabungan, dan Just dan Nothing apakah itu Maybe konstruktor. Kedua monads ini merangkum pola umum dan berguna dari perhitungan pada jenis data masing-masing (perhatikan bahwa keduanya tidak ada hubungannya dengan efek samping atau I / O).

Anda benar-benar harus bermain-main menulis beberapa kode Haskell non-sepele untuk menghargai apa monad tentang dan mengapa mereka berguna.


77



Anda harus terlebih dahulu memahami apa yang dimaksud dengan functor. Sebelum itu, pahami fungsi tingkat tinggi.

SEBUAH fungsi tingkat tinggi hanyalah sebuah fungsi yang mengambil fungsi sebagai argumen.

SEBUAH functor adalah jenis konstruksi apa pun T yang ada fungsi tingkat tinggi, sebut saja map, yang mengubah fungsi dari tipe a -> b (diberikan dua jenis a dan b) menjadi suatu fungsi T a -> T b. Ini map Fungsinya juga harus mematuhi hukum identitas dan komposisi sedemikian rupa sehingga ekspresi berikut kembali berlaku untuk semua p dan q (Notasi Haskell):

map id = id
map (p . q) = map p . map q

Misalnya, konstruktor jenis yang disebut List adalah functor jika dilengkapi dengan fungsi tipe (a -> b) -> List a -> List b yang mematuhi hukum di atas. Satu-satunya penerapan praktis adalah jelas. Hasilnya List a -> List b fungsi iterates atas daftar yang diberikan, memanggil (a -> b) berfungsi untuk setiap elemen, dan mengembalikan daftar hasil.

SEBUAH monad pada dasarnya hanya sebuah functor T dengan dua metode tambahan, join, tipe T (T a) -> T a, dan unit (kadang disebut return, fork, atau pure) tipe a -> T a. Untuk daftar di Haskell:

join :: [[a]] -> [a]
pure :: a -> [a]

Mengapa itu berguna? Karena Anda dapat, misalnya, map di atas daftar dengan fungsi yang mengembalikan daftar. Join mengambil daftar daftar hasil dan menggabungkannya. List adalah monad karena ini mungkin.

Anda dapat menulis fungsi yang berfungsi map, kemudian join. Fungsi ini disebut bind, atau flatMap, atau (>>=), atau (=<<). Ini biasanya bagaimana contoh monad diberikan di Haskell.

Monad harus memenuhi hukum tertentu, yaitu itu join harus bersifat asosiatif. Ini berarti bahwa jika Anda memiliki nilai x tipe [[[a]]] kemudian join (join x) harus sama join (map join x). Dan pure harus menjadi identitas untuk join seperti yang join (pure x) == x.


70



[Penafian: Saya masih mencoba untuk sepenuhnya grok monads. Berikut ini adalah apa yang saya pahami sejauh ini. Jika itu salah, semoga seseorang yang berpengetahuan akan memanggilku di karpet.]

Arnar menulis:

Monads hanyalah cara untuk membungkus sesuatu dan menyediakan metode untuk melakukan operasi pada barang yang dibungkus tanpa membukanya.

Itu tepatnya itu. Idenya seperti ini:

  1. Anda mengambil sejumlah nilai dan membungkusnya dengan beberapa informasi tambahan. Sama seperti nilai adalah jenis tertentu (mis. Integer atau string), jadi informasi tambahan adalah jenis tertentu.

    Misalnya, informasi tambahan itu mungkin a Maybe atau sebuah IO.

  2. Kemudian Anda memiliki beberapa operator yang memungkinkan Anda untuk beroperasi pada data yang dibungkus sambil membawa bersama informasi tambahan itu. Operator-operator ini menggunakan informasi tambahan untuk memutuskan bagaimana mengubah perilaku operasi pada nilai yang dibungkus.

    Misalnya, a Maybe Int bisa menjadi Just Int atau Nothing. Sekarang, jika Anda menambahkan Maybe Int ke a Maybe Int, operator akan memeriksa untuk melihat apakah keduanya Just Ints di dalam, dan jika demikian, akan membukanya Ints, berikan mereka operator tambahan, bungkus kembali hasilnya Int menjadi baru Just Int (yang valid Maybe Int), dan dengan demikian mengembalikan a Maybe Int. Tetapi jika salah satunya adalah a Nothing di dalam, operator ini akan segera kembali Nothing, yang lagi-lagi valid Maybe Int. Dengan begitu, Anda bisa berpura-pura bahwa Anda Maybe Inthanya angka normal dan melakukan matematika reguler pada mereka. Jika Anda mendapatkan Nothing, persamaan Anda masih akan menghasilkan hasil yang benar - tanpa Anda harus cek sampah Nothing dimana mana.

Tetapi contohnya adalah apa yang terjadi Maybe. Jika informasi tambahannya adalah IO, maka operator khusus itu ditentukan untuk IOs akan dipanggil sebagai gantinya, dan itu bisa melakukan sesuatu yang sama sekali berbeda sebelum melakukan penambahan. (Oke, tambahkan dua IO Ints bersama-sama mungkin tidak masuk akal - saya belum yakin.) (Juga, jika Anda memperhatikan Maybe Misalnya, Anda telah memperhatikan bahwa "membungkus nilai dengan barang ekstra" tidak selalu benar. Tetapi sulit untuk menjadi tepat, benar dan tepat tanpa harus dapat dipahami.)

Pada dasarnya, "Monad" kira-kira berarti "pola". Tapi bukannya sebuah buku yang penuh dengan penjelasan informal dan Pola bernama khusus, Anda sekarang punya sebuah konstruksi bahasa - sintaks dan semua - yang memungkinkan Anda melakukannya menyatakan pola baru sebagai hal-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 adalah istilah paling dekat yang diketahui dan dipahami kebanyakan orang.)

Dan itulah mengapa orang-orang menemukan monads begitu membingungkan: karena mereka adalah konsep generik. Untuk bertanya apa yang membuat sesuatu monad juga tidak jelas untuk bertanya apa yang membuat sesuatu menjadi sebuah pola.

Tapi pikirkan implikasi dari memiliki dukungan sintaksis dalam bahasa untuk gagasan pola: daripada harus membaca Geng Empat buku dan menghafal pembangunan pola tertentu, Anda saja tulis kode yang mengimplementasikan pola ini secara agnostik, generik 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 lagi dan lagi!

Jadi itulah mengapa orang yang memahami monads menemukannya begitu berguna: itu bukan konsep menara gading yang sombong intelektual menyombongkan diri pada pemahaman (OK, itu juga tentu saja, teehee), tetapi sebenarnya membuat kode lebih sederhana.


42



Setelah banyak berjuang, saya pikir saya akhirnya mengerti monad. Setelah membaca kembali kritik saya yang panjang tentang jawaban suara yang sangat banyak, saya akan menawarkan penjelasan ini.

Ada tiga pertanyaan yang perlu dijawab untuk memahami monada:

  1. Mengapa Anda membutuhkan monad?
  2. Apa itu monad?
  3. Bagaimana monad diterapkan?

Seperti yang saya catat dalam komentar asli saya, terlalu banyak penjelasan monad yang tertangkap dalam pertanyaan nomor 3, tanpa, dan sebelum benar-benar mencukupi pertanyaan 2, atau pertanyaan 1.

Mengapa Anda membutuhkan monad?

Bahasa fungsional murni seperti Haskell berbeda dari bahasa imperatif seperti C, atau Java, program fungsional murni tidak perlu dijalankan dalam urutan tertentu, satu langkah pada satu waktu. Program Haskell lebih mirip dengan fungsi matematika, di mana Anda dapat menyelesaikan "persamaan" dalam sejumlah potensial pesanan. Ini memberikan sejumlah manfaat, di antaranya adalah menghilangkan kemungkinan jenis bug tertentu, terutama yang berkaitan dengan hal-hal seperti "negara".

Namun, ada beberapa masalah yang tidak begitu mudah untuk dipecahkan dengan gaya pemrograman ini. Beberapa hal, seperti pemrograman konsol, dan file i / o, membutuhkan hal-hal terjadi dalam urutan tertentu, atau perlu mempertahankan keadaan. Salah satu cara untuk mengatasi masalah ini adalah membuat semacam objek yang mewakili keadaan komputasi, dan serangkaian fungsi yang mengambil objek status sebagai input, dan mengembalikan objek status yang dimodifikasi baru.

Jadi mari kita buat nilai "status" hipotetis, yang mewakili keadaan layar konsol. persis bagaimana nilai ini dibangun tidak penting, tetapi katakanlah itu adalah array dari panjang byte karakter ascii yang mewakili apa yang saat ini terlihat di layar, dan sebuah array yang mewakili baris terakhir dari input yang dimasukkan oleh pengguna, dalam pseudocode. Kami telah mendefinisikan beberapa fungsi yang menggunakan status konsol, memodifikasinya, dan mengembalikan status konsol baru.

consolestate MyConsole = new consolestate;

Jadi untuk melakukan pemrograman konsol, tetapi dengan cara yang murni fungsional, Anda perlu banyak melakukan panggilan fungsi di dalam eachother.

consolestate FinalConsole = print(input(print(myconsole, "Hello, what's your name?")),"hello, %inputbuffer%!");

Pemrograman dengan cara ini menjaga gaya fungsional "murni", sementara memaksa perubahan pada konsol terjadi dalam urutan tertentu. Namun, kami mungkin ingin melakukan lebih dari sekadar beberapa operasi pada saat seperti pada contoh di atas. Fungsi bersarang dengan cara itu akan mulai menjadi kaku. Yang kami inginkan, adalah kode yang pada dasarnya sama dengan yang di atas, tetapi ditulis sedikit lebih seperti ini:

consolestate FinalConsole = myconsole:
                            print("Hello, what's your name?"):
                            input():
                            print("hello, %inputbuffer%!");

Ini memang cara yang lebih mudah untuk menulisnya. Bagaimana kita melakukannya?

Apa itu monad?

Setelah Anda memiliki tipe (seperti consolestate) yang Anda definisikan bersama dengan sekelompok fungsi yang dirancang khusus untuk beroperasi pada jenis itu, Anda dapat mengubah seluruh paket dari hal-hal ini menjadi "monad" dengan mendefinisikan operator seperti : (bind) yang secara otomatis memberi umpan balik di sebelah kirinya, ke dalam parameter fungsi di sebelah kanannya, dan a lift operator yang mengubah fungsi normal menjadi fungsi yang berfungsi dengan operator bind jenis tertentu.

Bagaimana monad diterapkan?

Lihat jawaban lain, yang tampaknya cukup bebas untuk mengetahui detailnya.


37



(Lihat juga jawabannya di Apa itu monad?)

Motivasi yang baik untuk Monads adalah sigfpe (Dan Piponi) Anda Bisa Menciptakan Monads! (Dan Mungkin Anda Sudah Punya). Ada BANYAK tutorial monad lainnya, banyak di antaranya yang dengan salah berusaha menjelaskan monads dalam "istilah sederhana" menggunakan berbagai analogi: ini adalah kesalahan tutorial monad; Hindari mereka.

Seperti DR MacIver berkata dalam Beri tahu kami mengapa bahasa Anda payah:

Jadi, hal-hal yang saya benci tentang Haskell:

    Mari mulai dengan yang sudah jelas. Tutorial Monad. Tidak, bukan monads. Khususnya tutorialnya. Mereka adalah dewa yang tak berujung, yang berlebihan dan tercinta, mereka itu membosankan. Lebih lanjut, saya belum pernah melihat bukti yang meyakinkan bahwa mereka benar-benar membantu. Baca definisi kelas, tulis kode, dapatkan nama yang menakutkan.

Anda mengatakan Anda memahami monad Mungkin? Bagus, Anda sedang dalam perjalanan. Cukup mulai menggunakan monad lain dan cepat atau lambat Anda akan mengerti apa itu monads pada umumnya.

[Jika Anda berorientasi pada 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 "tipe konstruktor" yang mendefinisikan untuk setiap jenis "T" tipe baru "MT", dan beberapa cara untuk bolak-balik antara "biasa" jenis dan "M" jenis.]

Juga, cukup mengejutkan, salah satu perkenalan terbaik untuk monads sebenarnya adalah salah satu makalah akademis awal yang memperkenalkan monads, Philip Wadler's Monads untuk pemrograman fungsional. Ini sebenarnya praktis, tidak sepele memotivasi contoh, tidak seperti banyak tutorial buatan di luar sana.


33



Monad adalah, secara efektif, suatu bentuk "operator tipe". Itu akan melakukan tiga hal. Pertama ia akan "membungkus" (atau mengonversikan) nilai dari satu jenis ke tipe lain (biasanya disebut "tipe monadik"). Kedua itu akan membuat semua operasi (atau fungsi) tersedia pada jenis yang mendasarinya yang tersedia pada tipe monadik. Akhirnya itu akan memberikan dukungan untuk menggabungkan diri dengan monad lain untuk menghasilkan monad komposit.

"Mungkin monad" pada dasarnya sama dengan "tipe nullable" dalam Visual Basic / C #. Dibutuhkan jenis "T" non nullable dan mengubahnya menjadi "Nullable <T>", dan kemudian mendefinisikan apa yang semua operator biner berarti pada Nullable <T>.

Efek samping direpresentasikan secara simultan. Sebuah struktur dibuat yang berisi deskripsi efek samping bersama nilai pengembalian fungsi. Operasi "diangkat" lalu menyalin efek samping ketika nilai dilewatkan antar fungsi.

Mereka disebut "monads" daripada nama "operator" yang mudah dipahami karena beberapa alasan:

  1. Monad memiliki batasan pada apa yang bisa mereka lakukan (lihat definisi untuk rinciannya).
  2. Pembatasan tersebut, bersama dengan fakta bahwa ada tiga operasi yang terlibat, sesuai dengan struktur sesuatu yang disebut monad dalam Teori Kategori, yang merupakan cabang matematika yang tidak jelas.
  3. Mereka dirancang oleh pendukung bahasa fungsional "murni"
  4. Pendukung bahasa fungsional murni seperti cabang matematika yang tidak jelas
  5. Karena matematika itu tidak jelas, dan monad dikaitkan dengan gaya pemrograman tertentu, orang cenderung menggunakan kata monad sebagai semacam jabat tangan rahasia. Karena ini tidak ada yang mau repot-repot berinvestasi dalam nama yang lebih baik.

29