Mencoba memahami Monads. ›› Operator

Saya baru mengenal haskell dan saya belajar melalui LearnYouAHaskell. Saya tidak mengerti alasan di balik operator (>>).
Implementasi defaultnya adalah:

(>>) :: (Monad m) => m a -> m b -> m b  
m >> n = m >>= \_ -> n 

Yaitu (sejauh yang saya mengerti) mengabaikan nilai pertama dan mengembalikan nilai kedua. Namun, dari contoh di LearnYouAHaskell hal ini terjadi:

ghci> Tidak Ada >> Hanya 3
Tidak Ada
ghci> Hanya 3 >> Tidak Ada
Tidak Ada

Jadi tidak mengabaikan nilai pertama. Namun, dari sedikit riset saya menemukan kutipan ini dari di sini

Operator pengikatan fungsi >> mengabaikan nilai tindakan pertama dan mengembalikan hasil keseluruhan hanya hasil tindakan kedua saja.

Jadi saya bingung tentang penggunaan operator ini dan saya ingin menanyakan 2 hal:

  1. Apa sebenarnya fungsinya?
  2. Kapan berguna?

person Tzah Mama    schedule 20.07.2014    source sumber


Jawaban (5)


Fungsi >> hanya mengabaikan hasil dari nilai pertama namun tidak mengabaikan efek samping dari nilai pertama. Untuk memahami contoh Anda, lihat bagaimana Maybe Monad didefinisikan:

instance Monad Maybe where
  return = Just
  (Just x) >>= f = f x
  Nothing >>= _ = Nothing

Dan fungsi >> didefinisikan seperti ini:

m >> k      = m >>= \_ -> k

Nothing >> _ akan menghasilkan Nothing sesuai dengan definisi Maybe monad. Dalam contoh kedua Anda, Just 3 >> Nothing diperluas menjadi Just 3 >>= \_ -> Nothing dan menghasilkan Nothing. Untuk memberi Anda contoh bagaimana tindakan tersebut hanya mengabaikan nilai tindakan pertama namun tidak mengabaikan efek sampingnya, pertimbangkan contoh berikut:

λ> print 3 >> print 4
3
4

Terlihat pada contoh di atas, meskipun mengabaikan hasil print 3 yaitu () namun tidak mengabaikan efek sampingnya yaitu menampilkan 3 ke layar.

Fungsi >> menjadi berguna setelah Anda mulai menggunakan perpustakaan Haskell lainnya. Dua tempat di mana saya kadang-kadang menggunakannya adalah ketika berhadapan dengan Parser (parsec, attoparsec) dan perpustakaan Pipes.

person Sibi    schedule 20.07.2014

Ini mengabaikan nilai tindakan pertama, bukan tindakan itu sendiri.

Just 3 >> Just 5

Nilai tindakan Just 3 adalah 3. Itu diabaikan di bagian \_ -> n. Hasil keseluruhannya adalah Just 5.

Just 3 >> Nothing

Nilai tindakan Just 3 adalah 3. Itu diabaikan di bagian \_ -> n. Hasil keseluruhannya adalah Nothing.

Nothing >> Just 3

Tindakan Nothing tidak menghasilkan nilai apa pun. Lalu apa yang diteruskan ke operan kanan >>= (atau >>)? Tidak! >>= untuk monad Maybe dibuat sedemikian rupa sehingga jika tindakan kiri adalah Nothing, tindakan kanan tidak dijalankan sama sekali, dan hasil keseluruhannya adalah Nothing.

person n. 1.8e9-where's-my-share m.    schedule 20.07.2014

Untuk melengkapi jawaban Sibi >> bisa dilihat sebagai ; dalam bahasa lain seperti C atau C++..

Ketika Anda melakukannya di C (atau setara dalam bahasa lain)

printf("foo"); printf("batang");

Anda jelas mencetak foobar (efek samping) tetapi panggilan ke printf juga memiliki nilai kembalian, yang dalam kasus kami adalah panjang yang dicetak yaitu 3 3. Pernahkah Anda bertanya-tanya apa yang terjadi dengan angka-angka itu? Mereka dibuang, karena di C, expr 1; exp 2, artinya

  • evaluasi expr1
  • membuang hasilnya
  • evaluasi expr2

(Pada saat itu, Anda dapat bertanya pada diri sendiri mengapa kompiler harus repot mengevaluasi expr1 jika ingin membuang hasilnya? Karena efek sampingnya. Dalam kasus printf efek sampingnya adalah mencetak sesuatu. Anda jarang tertarik pada nilai yang dikembalikan itu sendiri.)

Jadi ; dapat dilihat sebagai operator yang mengambil 2 ekspresi dan mengembalikan ekspresi baru. Ini persis sama dengan apa yang dilakukan operator >>.

Saat Anda menulis

 print "foo" >> print "bar"

itu persis setara dengan printf("foo");printf("bar") kecuali (dan itu perbedaan besar) >> bukanlah sesuatu yang ajaib seperti ; di C. >> ini adalah operator yang ditentukan pengguna dan dapat didefinisikan ulang untuk setiap jenis Monad. Inilah sebabnya pemrogram Haskell sangat menyukai Monad : Singkatnya, ini memungkinkan Anda mendefinisikan ulang perilaku ; Anda sendiri.

Seperti yang kita lihat, di C ; cukup evaluasi ekspresi dan buang nilainya. Faktanya, ini sedikit lebih rumit karena tidak akan terjadi jika itu adalah break atau return. Monad Nothing in the Maybe dapat dilihat sebagai break atau return. >> mengevaluasi ekspresi pertama, dan berhenti jika Nothing. Kalau tidak, ia akan membuang nilainya dan melanjutkan.

Contoh pertama Anda dapat dilihat di C (menurut saya ini C yang valid)

3; return

Dan

return; 3

Contoh pertama, hitung 3, buang nilainya dan kembalikan. Yang kedua, langsung kembali.

Untuk menjawab pertanyaan Anda when is it usefull ? Hampir sepanjang waktu ketika Anda menggunakan IO, meskipun Anda jarang melihatnya.

Daripada menulis

 print "foo" >> print "bar"

Haskell menyediakan gula sintaksis yang mengubah (hampir) baris baru menjadi >> melalui notasi do, jadi Anda akan menulis

do
  print "foo"
  print "bar"

yang benar-benar setara dengan versi sebelumnya (Faktanya, versi notasi do diubah ke versi sebelumnya oleh kompiler).

Bahkan juga setara dengan (walaupun jarang digunakan)

do print "foo"; print "bar"

Ringkasnya, >> dapat dilihat setara dengan ; atau baris baru dalam bahasa lain dengan perbedaan bahwa arti sebenarnya bergantung pada konteks (di mana Monad bertindak). >> di Mungkin monad berbeda dari >> di IO Monad.

person mb14    schedule 20.07.2014

Pertama-tama, mari hilangkan kebingungan Anda tentang Maybe monad. Pertimbangkan hal berikut ini

instance Monad Maybe where
  return = Just
  (Just x) >>= g = g x
  Nothing  >>= _ = Nothing

Seperti yang Anda lihat menurut definisinya, Nothing >>= _ adalah Nothing. Karena >> hanyalah kasus khusus dari >>= yang parameternya diabaikan, hasilnya sama.

Hal ini karena Maybes biasanya digunakan untuk merepresentasikan komputasi yang mungkin gagal. Hal ini untuk memberitahu kita bahwa "Sekali Anda gagal, Anda selalu gagal".

Sekarang untuk menjawab pertanyaan Anda.

  1. Kutipan yang Anda sebutkan sudah menjawabnya.
  2. Hal ini berguna dalam beberapa situasi di mana hasil dari tindakan tersebut tidak diperlukan. Misalnya, hasil dari putStrLn adalah (), yang tidak menarik dan tidak berguna dalam hal apa pun.
person Evan Sebastian    schedule 20.07.2014

Alasannya sederhana: dengan kata sederhana ada dua operasi yang diperlukan untuk implementasi monad:

  • >>=
  • >>

Yang pertama mengeksekusi tindakan dan meneruskan hasil tindakannya ke tindakan lain. Misalnya:

Prelude> :t getLine
getLine :: IO String
Prelude> :t putStrLn
putStrLn :: String -> IO ()

Dua fungsi: pertama getLine hanya mengembalikan String yang dibungkus dengan IO, ia membaca string dari stdin dan membungkusnya ke IO. putStrLn kedua mendapatkan String dan mencetaknya. Bind memiliki tipe berikut:

:t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

Ini dapat direpresentasikan sebagai IO String -> (String -> IO String) -> IO String, sehingga Anda dapat menggabungkan dua fungsi atau lebih ini dengan (>>=) dengan mengeksekusi getLine dan meneruskan hasil String ke putStrLn:

getLine >>= putStrLn

Jadi Anda bisa menggabungkan dua fungsi atau lebih ini dalam satu tindakan.

>> kedua menghasilkan hasil yang hampir sama, tetapi tidak memerlukan hasil tindakan sebelumnya.

:t (>>)
(>>) :: Monad m => m a -> m b -> m b

Itu hanya mengeksekusi, tindakan pertama, dan kemudian tindakan kedua, dan tindakan kedua tidak diperlukan sebagai hasil dari tindakan pertama:

putStrLn "Hello" >> putStrLn "World"
person 0xAX    schedule 20.07.2014