Bagaimana Anda mengucapkan fungsi-fungsi ini di kelas tipe Aplikatif:
(<*>) :: f (a -> b) -> f a -> f b
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
(Artinya, jika mereka bukan operator, mereka akan dipanggil apa?)
Sebagai catatan tambahan, jika Anda dapat mengganti nama pure
menjadi sesuatu yang lebih bersahabat dengan non-matematikawan, apa yang akan Anda sebut?
pure
mungkinmakeApplicative
.pure
saran Anda sebagai jawaban dan saya akan memberi suara positifJawaban:
Mengetahui matematika Anda, atau tidak, sebagian besar tidak relevan di sini, menurut saya. Seperti yang mungkin Anda sadari, Haskell meminjam beberapa bit terminologi dari berbagai bidang matematika abstrak, terutama Teori Kategori , dari mana kita mendapatkan functor dan monad. Penggunaan istilah-istilah ini di Haskell agak menyimpang dari definisi matematika formal, tetapi mereka biasanya cukup dekat untuk menjadi istilah deskriptif yang baik.
Kelas
Applicative
tipe berada di antaraFunctor
danMonad
, jadi orang akan mengharapkannya memiliki dasar matematika yang serupa. Dokumentasi untukControl.Applicative
modul dimulai dengan:Hmm.
Tidak semenarik itu
Monad
, menurut saya.Apa yang pada dasarnya semua ini intinya adalah itu
Applicative
tidak sesuai dengan konsep apa pun yang sangat menarik secara matematis, jadi tidak ada istilah siap pakai yang menangkap cara penggunaannya di Haskell. Jadi, kesampingkan matematika untuk saat ini.Jika kita ingin tahu apa yang disebut
(<*>)
, mungkin membantu untuk mengetahui apa artinya pada dasarnya.Jadi apa dengan
Applicative
, pula, dan mengapa tidak kita menyebutnya itu?Apa yang
Applicative
berjumlah dalam praktek adalah cara untuk mengangkat sewenang-wenang fungsi menjadiFunctor
. Pertimbangkan kombinasiMaybe
(bisa dibilang yang paling sederhana non-sepeleFunctor
) danBool
(juga tipe data non-sepele yang paling sederhana).Fungsinya
fmap
memungkinkan kita mengangkatnot
dari bekerjaBool
ke mengerjakanMaybe Bool
. Tetapi bagaimana jika kita ingin mengangkat(&&)
?Nah, itu bukan apa yang kita inginkan sama sekali ! Faktanya, itu sangat tidak berguna. Kita bisa mencoba menjadi pandai dan menyelinap
Bool
masukMaybe
melalui belakang ...... tapi itu tidak bagus. Untuk satu hal, itu salah. Untuk hal lain, itu jelek . Kami dapat terus mencoba, tetapi ternyata tidak ada cara untuk mengangkat fungsi dari beberapa argumen untuk bekerja secara sembarangan
Functor
. Menyebalkan!Di sisi lain, kita bisa melakukannya dengan mudah jika kita menggunakan
Maybe
'sMonad
contoh:Nah, itu sangat merepotkan hanya untuk menerjemahkan fungsi sederhana - itulah sebabnya
Control.Monad
menyediakan fungsi untuk melakukannya secara otomatis ,liftM2
. Angka 2 dalam namanya mengacu pada fakta bahwa ia bekerja pada fungsi tepat dua argumen; fungsi serupa ada untuk 3, 4, dan 5 fungsi argumen. Fungsi-fungsi ini lebih baik , tetapi tidak sempurna, dan menentukan jumlah argumen itu jelek dan kikuk.Yang membawa kita ke makalah yang memperkenalkan kelas tipe Aplikatif . Di dalamnya, pada dasarnya penulis melakukan dua pengamatan:
Functor
adalah hal yang sangat wajar untuk dilakukanMonad
Aplikasi fungsi normal ditulis dengan penjajaran istilah yang sederhana, sehingga untuk membuat "aplikasi yang diangkat" sesederhana dan sealami mungkin, makalah ini memperkenalkan operator infix untuk menggantikan aplikasi, diangkat ke dalam
Functor
, dan kelas tipe untuk menyediakan apa yang dibutuhkan untuk itu .Semuanya membawa kita ke poin berikut:
(<*>)
hanya mewakili aplikasi fungsi - jadi mengapa mengucapkannya secara berbeda dari yang Anda lakukan pada "operator penjajaran" spasi kosong?Tetapi jika itu tidak terlalu memuaskan, kita dapat mengamati bahwa
Control.Monad
modul juga menyediakan fungsi yang melakukan hal yang sama untuk monad:Dimana
ap
, tentu saja, singkatan dari "melamar". Karena apapunMonad
bisaApplicative
, danap
hanya membutuhkan subset dari fitur yang ada pada yang terakhir, kita mungkin bisa mengatakan bahwa jika(<*>)
bukan operator, itu harus dipanggilap
.Kita juga bisa mendekati sesuatu dari arah lain. The
Functor
operasi pengangkatan disebutfmap
karena itu generalisasi darimap
operasi pada daftar. Seperti apa fungsi pada daftar yang akan berfungsi(<*>)
? Ada apa yangap
ada di daftar, tentu saja, tapi itu tidak terlalu berguna.Faktanya, mungkin ada interpretasi yang lebih alami untuk daftar. Apa yang terlintas dalam pikiran saat Anda melihat tanda tangan tipe berikut?
Ada sesuatu yang begitu menggoda tentang gagasan melapisi daftar secara paralel, menerapkan setiap fungsi di bagian pertama ke elemen yang sesuai di kedua. Sayangnya bagi teman lama kita
Monad
, operasi sederhana ini melanggar hukum monad jika panjang daftarnya berbeda. Tapi itu membuat dendaApplicative
, dalam hal ini(<*>)
menjadi cara merangkai versi umumzipWith
, jadi mungkin kita bisa membayangkan menyebutnyafzipWith
?Ide membuat ritsleting ini benar-benar memberi kita lingkaran penuh. Ingat hal matematika sebelumnya, tentang fungsi monoid? Seperti namanya, berikut adalah cara untuk menggabungkan struktur monoid dan fungsi, keduanya adalah kelas tipe Haskell yang sudah dikenal:
Akan terlihat seperti apa jika Anda memasukkannya ke dalam kotak dan menggoyangnya sedikit? Dari
Functor
kita akan menjaga gagasan tentang struktur tidak tergantung pada parameter tipenya , dan dariMonoid
kita akan mempertahankan bentuk fungsi secara keseluruhan:Kami tidak ingin berasumsi bahwa ada cara untuk membuat yang benar-benar "kosong"
Functor
, dan kami tidak dapat membayangkan nilai dari sembarang tipe, jadi kami akan memperbaiki tipemfEmpty
sebagaif ()
.Kami juga tidak ingin memaksa
mfAppend
untuk membutuhkan parameter tipe yang konsisten, jadi sekarang kami memiliki ini:Untuk apa jenis hasil tersebut
mfAppend
? Kami memiliki dua tipe arbitrer yang tidak kami ketahui, jadi kami tidak memiliki banyak pilihan. Hal yang paling masuk akal adalah menyimpan keduanya:Pada titik mana
mfAppend
sekarang jelas merupakan versi umum darizip
daftar, dan kami dapat merekonstruksiApplicative
dengan mudah:Ini juga menunjukkan kepada kita yang
pure
terkait dengan elemen identitas aMonoid
, jadi nama bagus lainnya untuk itu mungkin apa pun yang menyarankan nilai unit, operasi nol, atau semacamnya.Itu sangat panjang, jadi untuk meringkas:
(<*>)
hanyalah sebuah aplikasi fungsi yang dimodifikasi, jadi Anda dapat membacanya sebagai "ap" atau "apply", atau elide seluruhnya seperti yang Anda lakukan pada aplikasi fungsi normal.(<*>)
juga secara kasar menggeneralisasizipWith
daftar, sehingga Anda dapat membacanya sebagai "fungsi zip dengan", mirip dengan membacafmap
sebagai "memetakan fungsi dengan".Yang pertama lebih dekat dengan maksud dari
Applicative
kelas tipe - seperti namanya - jadi itulah yang saya rekomendasikan.Faktanya, saya mendorong penggunaan liberal, dan non-pengucapan, dari semua operator aplikasi yang diangkat :
(<$>)
, yang mengangkat fungsi argumen tunggal menjadi aFunctor
(<*>)
, yang menghubungkan fungsi multi-argumen melaluiApplicative
(=<<)
, yang mengikat fungsi yang memasukkan aMonad
ke komputasi yang sudah adaKetiganya, pada intinya, hanya aplikasi fungsi biasa, dibumbui sedikit.
sumber
Applicative
dan gaya idiomatik fungsional yang dipromosikannya tidak mendapatkan cukup cinta, jadi saya tidak bisa menahan kesempatan untuk sedikit memuji kebajikannya sebagai sarana untuk menjelaskan bagaimana saya (tidak) mengucapkannya(<*>)
.Applicative
's! Sesuatu seperti[| f a b c d |]
(seperti yang disarankan oleh kertas asli). Maka kami tidak memerlukan<*>
kombinator dan Anda akan merujuk ke ekspresi seperti itu sebagai contoh "aplikasi fungsi dalam konteks fungsi"Monad
. AtauFunctor
atauMonoid
apa pun yang memiliki istilah mapan yang melibatkan kurang dari tiga kata sifat. "Aplikatif" hanyalah nama yang tidak menginspirasi, meskipun cukup deskriptif, yang ditampar ke sesuatu yang agak dibutuhkan.Karena saya tidak memiliki ambisi untuk meningkatkan jawaban teknis CA McCann , saya akan menangani jawaban yang lebih halus:
Sebagai alternatif, terutama karena tidak ada akhir dari seruan terus menerus yang dipenuhi kecemasan dan pengkhianatan terhadap
Monad
versi, yang disebut "return
", saya mengusulkan nama lain, yang menyarankan fungsinya dengan cara yang dapat memuaskan pemrogram yang paling penting , dan yang paling fungsional ... baik, mudah-mudahan, semua orang bisa mengeluh sama tentang:inject
.Ambil nilai. "Inject" ke dalam
Functor
,Applicative
,Monad
, atau apa-Anda-. Saya memilih "inject
", dan saya menyetujui pesan ini.sumber
inject
adalah nama yang bagus dan mungkin lebih baik dari nama saya, meskipun sebagai catatan kecil, "inject" digunakan dalam - menurut saya - Smalltalk dan Ruby untuk metode lipatan kiri. Aku tidak pernah mengerti pilihan nama itu, meskipun ...inject
di Ruby & Smalltalk digunakan karena ini seperti Anda "menyuntikkan" operator di antara setiap elemen dalam daftar. Setidaknya, itulah yang selalu saya pikirkan.foldr
. (Anda mengganti(:)
dan[]
, di mana(:)
mengambil 2 argumen dan[]
merupakan konstanta, makafoldr (+) 0 (1:2:3:[])
↝1+2+3+0
.) DiBool
atasnya hanyaif
-then
-else
(dua konstanta, pilih satu) dan untukMaybe
itu disebutmaybe
... Haskell tidak memiliki nama / fungsi tunggal untuk ini, karena semua memiliki tipe yang berbeda (secara umum elim hanya rekursi / induksi)Secara singkat:
<*>
Anda bisa menyebutnya berlaku . JadiMaybe f <*> Maybe a
dapat diucapkan sebagai berlakuMaybe f
atasMaybe a
.Anda dapat mengganti nama
pure
menjadiof
, seperti yang dilakukan banyak pustaka JavaScript. Di JS Anda dapat membuatMaybe
denganMaybe.of(a)
.Juga, wiki Haskell memiliki halaman tentang pengucapan operator bahasa di sini
sumber
Sumber: Pemrograman Haskell dari First Principles , oleh Chris Allen dan Julie Moronuki
sumber