Monad pembaca begitu kompleks dan sepertinya tidak berguna. Dalam bahasa imperatif seperti Java atau C ++, tidak ada padanan konsep untuk reader monad, kalau saya tidak salah.
Dapatkah Anda memberi saya contoh sederhana dan menjelaskannya sedikit?
haskell
monads
reader-monad
chipbk10
sumber
sumber
Jawaban:
Jangan takut! Reader monad sebenarnya tidak terlalu rumit, dan memiliki utilitas yang sangat mudah digunakan.
Ada dua cara untuk mendekati monad: kita bisa bertanya
Dari pendekatan pertama, monad pembaca adalah beberapa tipe abstrak
seperti yang
Jadi bagaimana kita menggunakan ini? Nah, reader monad bagus untuk meneruskan informasi konfigurasi (implisit) melalui komputasi.
Setiap kali Anda memiliki "konstanta" dalam komputasi yang Anda perlukan pada berbagai titik, tetapi sebenarnya Anda ingin dapat melakukan komputasi yang sama dengan nilai yang berbeda, Anda harus menggunakan pembaca monad.
Reader monads juga digunakan untuk melakukan apa yang oleh orang-orang OO disebut injeksi ketergantungan . Misalnya, algoritme negamax sering digunakan (dalam bentuk yang sangat dioptimalkan) untuk menghitung nilai posisi dalam permainan dua pemain. Algoritme itu sendiri tidak peduli game apa yang Anda mainkan, kecuali Anda harus dapat menentukan apa posisi "selanjutnya" dalam game, dan Anda harus dapat mengetahui apakah posisi saat ini adalah posisi kemenangan.
Ini kemudian akan bekerja dengan permainan dua pemain yang terbatas dan deterministik.
Pola ini berguna bahkan untuk hal-hal yang sebenarnya bukan injeksi ketergantungan. Misalkan Anda bekerja di bidang keuangan, Anda mungkin merancang beberapa logika rumit untuk menentukan harga suatu aset (kata turunan), yang semuanya baik dan bagus dan Anda dapat melakukannya tanpa monad yang bau. Tetapi kemudian, Anda memodifikasi program Anda untuk menangani banyak mata uang. Anda harus bisa menukar mata uang dengan cepat. Upaya pertama Anda adalah menentukan fungsi tingkat atas
untuk mendapatkan harga spot. Anda kemudian dapat memanggil kamus ini dalam kode Anda .... tapi tunggu! Itu tidak akan berhasil! Kamus mata uang tidak dapat diubah dan karenanya harus sama tidak hanya untuk masa pakai program Anda, tetapi sejak saat itu dikompilasi ! Jadi apa yang kamu lakukan? Nah, salah satu opsinya adalah menggunakan Reader monad:
Mungkin kasus penggunaan paling klasik adalah dalam mengimplementasikan penerjemah. Tapi, sebelum kita melihatnya, kita perlu memperkenalkan fungsi lain
Oke, jadi Haskell dan bahasa fungsional lainnya didasarkan pada kalkulus lambda . Kalkulus Lambda memiliki sintaks yang mirip
dan kami ingin menulis evaluator untuk bahasa ini. Untuk melakukannya, kita perlu melacak lingkungan, yang merupakan daftar binding yang terkait dengan istilah (sebenarnya ini akan menjadi closure karena kita ingin melakukan pelingkupan statis).
Ketika kita selesai, kita harus mendapatkan nilai (atau kesalahan):
Jadi, mari kita tulis juru bahasa:
Akhirnya, kita bisa menggunakannya dengan melewatkan lingkungan sepele:
Dan itu dia. Penerjemah yang berfungsi penuh untuk kalkulus lambda.
Cara lain untuk memikirkan hal ini adalah dengan bertanya: Bagaimana penerapannya? Jawabannya adalah reader monad sebenarnya adalah salah satu monad yang paling sederhana dan paling elegan.
Pembaca hanyalah nama keren untuk fungsi! Kami telah mendefinisikan
runReader
jadi bagaimana dengan bagian lain dari API? Nah, setiapMonad
juga merupakanFunctor
:Sekarang, untuk mendapatkan monad:
yang tidak terlalu menakutkan.
ask
sangat sederhana:sementara
local
tidak terlalu buruk:Oke, jadi pembaca monad hanyalah sebuah fungsi. Mengapa memiliki Pustaka? Pertanyaan bagus. Sebenarnya, Anda tidak membutuhkannya!
Ini bahkan lebih sederhana. Terlebih lagi,
ask
hanyaid
danlocal
hanyalah komposisi fungsi dengan urutan fungsi yang dialihkan!sumber
Reader
apakah fungsi dengan implementasi tertentu dari kelas tipe monad? Mengatakannya lebih awal akan membantu saya untuk tidak terlalu bingung. Pertama saya tidak mengerti. Di tengah jalan, saya berpikir "Oh, ini memungkinkan Anda mengembalikan sesuatu yang akan memberi Anda hasil yang diinginkan begitu Anda memberikan nilai yang hilang." Saya pikir itu berguna, tetapi tiba-tiba menyadari bahwa suatu fungsi melakukan hal ini.local
fungsi tersebut membutuhkan penjelasan lebih meskipun ..(Reader f) >>= g = (g (f x))
?x
?Saya ingat pernah bingung seperti Anda, sampai saya menemukan sendiri bahwa varian Monad Reader ada di mana - mana . Bagaimana saya menemukannya? Karena saya terus menulis kode yang ternyata variasi kecil di atasnya.
Misalnya, pada satu titik saya sedang menulis beberapa kode untuk menangani nilai - nilai sejarah ; nilai-nilai yang berubah seiring waktu. Model yang sangat sederhana ini adalah fungsi dari titik waktu ke nilai pada titik waktu tersebut:
The
Applicative
contoh berarti bahwa jika Anda memilikiemployees :: History Day [Person]
dancustomers :: History Day [Person]
Anda dapat melakukan ini:Yaitu,
Functor
danApplicative
memungkinkan kita untuk menyesuaikan fungsi reguler, non-historis untuk bekerja dengan sejarah.Contoh monad paling intuitif dipahami dengan mempertimbangkan fungsinya
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
. Fungsi tipea -> History t b
adalah fungsi yang memetakan filea
sejarahb
nilai; misalnya, Anda dapat memilikigetSupervisor :: Person -> History Day Supervisor
, dangetVP :: Supervisor -> History Day VP
. Jadi contoh MonadHistory
adalah tentang menyusun fungsi seperti ini; misalnya,getSupervisor >=> getVP :: Person -> History Day VP
adalah fungsi yang mendapatkan, untuk apa punPerson
, riwayatVP
yang mereka miliki.Nah, ini
History
sebenarnya monad ini sama persis denganReader
.History t a
benar-benar sama denganReader t a
(yang sama dengant -> a
).Contoh lain: Saya telah membuat prototipe desain OLAP di Haskell baru-baru ini. Salah satu ide di sini adalah dari "hypercube", yang merupakan pemetaan dari persimpangan sekumpulan dimensi ke nilai. Baiklah, kita lanjut lagi:
Salah satu operasi yang umum pada hypercube adalah menerapkan fungsi skalar multi-tempat ke titik-titik hypercube yang sesuai. Ini bisa kita dapatkan dengan mendefinisikan
Applicative
contoh untukHypercube
:Saya baru saja menyalin
History
kode di atas dan mengubah nama. Seperti yang Anda tahu,Hypercube
itu juga adilReader
.Ini terus berlanjut. Misalnya, penerjemah bahasa juga memiliki intisari
Reader
, ketika Anda menerapkan model ini:Reader
ask
Reader
lingkungan pelaksanaan.local
Sebuah analogi yang baik adalah bahwa a
Reader r a
mewakili dana
dengan "lubang" di dalamnya, yang mencegah Anda untuk mengetahui yanga
sedang kita bicarakan. Anda hanya bisa mendapatkan aktuala
setelah Anda menyediakan danr
untuk mengisi lubang. Ada banyak sekali hal seperti itu. Dalam contoh di atas, "histori" adalah nilai yang tidak dapat dihitung hingga Anda menentukan waktu, hypercube adalah nilai yang tidak dapat dihitung hingga Anda menentukan perpotongan, dan ekspresi bahasa adalah nilai yang dapat tidak akan dihitung sampai Anda memberikan nilai variabel. Ini juga memberi Anda intuisi tentang mengapaReader r a
sama denganr -> a
, karena fungsi seperti itu juga secara intuitif merupakan suatu yanga
hilangr
.Jadi
Functor
,Applicative
danMonad
contoh dariReader
adalah generalisasi yang sangat berguna untuk kasus di mana Anda memodelkan apa pun dari semacam "ana
yang kehilanganr
," dan memungkinkan Anda untuk memperlakukan objek "tidak lengkap" ini seolah-olah mereka lengkap.Cara lain untuk mengatakan hal yang sama: a
Reader r a
adalah sesuatu yang mengkonsumsir
dan menghasilkana
, danFunctor
,Applicative
danMonad
contoh adalah pola dasar untuk bekerja denganReader
s.Functor
= membuat aReader
yang mengubah keluaran dari yang lainReader
;Applicative
= hubungkan duaReader
s ke input yang sama dan gabungkan outputnya;Monad
= memeriksa hasil dari aReader
dan menggunakannya untuk membuat yang lainReader
. Thelocal
andwithReader
functions = make aReader
yang mengubah input ke input lainnyaReader
.sumber
GeneralizedNewtypeDeriving
ekstensi untuk menurunkanFunctor
,Applicative
,Monad
, dll untuk newtypes berdasarkan jenis yang mendasari mereka.Di Java atau C ++ Anda dapat mengakses variabel apa pun dari mana saja tanpa masalah. Masalah muncul ketika kode Anda menjadi multi-threaded.
Di Haskell Anda hanya memiliki dua cara untuk meneruskan nilai dari satu fungsi ke fungsi lainnya:
fn1 -> fn2 -> fn3
fungsifn2
mungkin tidak memerlukan parameter yang Anda teruskanfn1
kefn3
.Monad Pembaca hanya meneruskan data yang ingin Anda bagi antar fungsi. Fungsi dapat membaca data itu, tetapi tidak dapat mengubahnya. Itu saja yang dilakukan Reader monad. Hampir semuanya. Ada juga sejumlah fungsi seperti
local
, tetapi untuk pertama kalinya Anda hanya dapat menggunakanasks
.sumber
do
-notasi, yang akan lebih baik jika difaktor ulang menjadi fungsi murni.where
klausa, apakah itu akan diterima sebagai cara ke-3 untuk melewatkan variabel?