Terjemahkan dari monad ke aplikatif

Oke, jadi saya tahu apa isi kelas tipe Applicative, dan mengapa itu berguna. Tapi saya tidak bisa memikirkan bagaimana Anda akan menggunakannya dalam contoh yang tidak sepele.

Misalnya saja parser Parsec sederhana berikut ini:

integer :: Parser Integer
integer = do
  many1 space
  ds <- many1 digit
  return $ read ds

Sekarang bagaimana Anda menulisnya tanpa menggunakan instance Monad untuk Parser? Banyak orang mengklaim bahwa ini bisa dilakukan dan merupakan ide bagus, tapi saya tidak tahu bagaimana tepatnya.


person MathematicalOrchid    schedule 27.02.2013    source sumber


Jawaban (3)


integer :: Parser Integer
integer = read <$> (many1 space *> many1 digit)

Or

integer = const read <$> many1 space <*> many1 digit

Apakah menurut Anda salah satu dari ini lebih mudah dibaca, itu terserah Anda.

person dave4420    schedule 27.02.2013
comment
Kita ingin mengabaikan nilai (tetapi bukan efeknya) dari many1 space, dan menerapkan read pada nilai many1 digit. (Maaf, saya baru masuk, terlambat, saya lelah: Saya bermain cepat dan longgar dengan terminologi.) Jika Anda membayangkan s dan d masing-masing mewakili nilai many1 space dan many1 digit, maka nilainya (abaikan efek) dari const read <$> many1 space <*> many1 digit adalah const read s d = read d. - person dave4420; 01.03.2013

saya akan menulis

integer :: Parser Integer
integer = read <$ many1 space <*> many1 digit

Ada sekelompok operator pembuat parser asosiatif kiri (seperti aplikasi) <$>, <*>, <$, <*. Yang paling kiri adalah fungsi murni yang mengumpulkan nilai hasil dari nilai komponen. Benda di sebelah kanan setiap operator harus berupa parser, yang secara kolektif memberikan komponen tata bahasa dari kiri ke kanan. Operator mana yang digunakan bergantung pada dua pilihan, sebagai berikut.

  the thing to the right is    signal  / noise
  _________________________            
  the thing to the left is \           
                            +-------------------
                    pure /  |   <$>       <$
                  a parser  |   <*>       <*

Jadi, setelah memilih read :: String -> Integer sebagai fungsi murni yang akan menyampaikan semantik parser, kita dapat mengklasifikasikan spasi terdepan sebagai "noise" dan sekumpulan digit sebagai "sinyal", maka

 read <$ many1 space <*> many1 digit
 (..)    (.........)     (.........)
 pure    noise parser     |
 (.................)      |
     parser              signal parser
 (.................................)
                    parser

Anda dapat menggabungkan berbagai kemungkinan dengan

p1 <|> ... <|> pn

dan menyatakan ketidakmungkinan dengan

empty

Jarang diperlukan untuk memberi nama komponen dalam parser, dan kode yang dihasilkan lebih terlihat seperti tata bahasa dengan tambahan semantik.

person pigworker    schedule 27.02.2013
comment
Wow, saya tahu tentang <$, tapi saya hanya menggunakannya jika di sebelah kirinya adalah konstanta dan di sebelah kanannya adalah nilai sederhana... Saya tidak pernah memikirkan apa yang akan terjadi jika saya meletakkan suatu fungsi di sebelah kiri: P Trik yang bagus - person Niklas B.; 28.02.2013

Contoh Anda dapat ditulis ulang secara bertahap ke bentuk yang lebih mirip dengan Applicative:

do
  many1 space
  ds <- many1 digit
  return $ read ds
  1. definisi notasi do:

    many1 space >> (many1 digit >>= \ds -> return $ read ds)
    
  2. definisi $:

    many1 space >> (many1 digit >>= \ds -> return (read ds))
    
  3. definisi .:

    many1 space >> (many1 digit >>= (return . read))
    
  4. Hukum monad ke-3 (asosiasi):

    (many1 space >> many1 digit) >>= (return . read)
    
  5. definisi liftM (dalam notasi non-do):

    liftM read (many1 space >> many1 digit)
    

Ini (atau seharusnya, jika saya tidak mengacaukannya :)) perilakunya identik dengan contoh Anda.

Sekarang, jika Anda mengganti liftM dengan fmap dengan <$>, dan >> dengan *>, Anda mendapatkan Applicative:

read <$> (many1 space *> many1 digit)

Ini valid karena liftM, fmap, dan <$> umumnya dianggap sinonim, begitu pula >> dan *>.

Ini semua berfungsi dan kita dapat melakukan ini karena contoh asli tidak menggunakan hasil parser apa pun untuk membuat parser berikut.

person Matt Fenwick    schedule 01.03.2013
comment
Dingin! Cara lain untuk menulis read <$ many1 space <*> many1 digit. :) Kalimat terakhir sangat penting. Apakah itu berarti gaya ini sesuai dengan tata bahasa bebas konteks, dan tata bahasa yang lebih umum harus diuraikan dengan gaya monadik? - person Will Ness; 05.03.2013
comment
@WillNess Saya bukan ahli dalam hal ini, tapi saya yakin itulah masalahnya. - person Matt Fenwick; 05.03.2013