Seperti biasa, terminologi yang digunakan orang tidak sepenuhnya konsisten. Ada berbagai gagasan yang terinspirasi oleh monad tetapi secara tegas tidak cukup. Istilah "monad terindeks" adalah salah satu angka (termasuk "monadish" dan "monad berparameter" (nama Atkey untuk mereka)) dari istilah yang digunakan untuk menandai salah satu gagasan tersebut. (Gagasan lain seperti itu, jika Anda tertarik, adalah "efek parametrik monad" Katsumata, diindeks oleh monoid, di mana pengembalian diindeks secara netral dan ikatan terakumulasi dalam indeksnya.)
Pertama-tama, mari kita periksa jenisnya.
IxMonad (m :: state -> state -> * -> *)
Artinya, jenis "komputasi" (atau "tindakan", jika Anda mau, tetapi saya akan tetap menggunakan "komputasi"), terlihat seperti
m before after value
dimana before, after :: state
dan value :: *
. Idenya adalah untuk menangkap sarana untuk berinteraksi secara aman dengan sistem eksternal yang memiliki beberapa gagasan keadaan yang dapat diprediksi . Jenis komputasi memberi tahu Anda keadaan apa yang harus before
dijalankannya, keadaan apa yang akan after
dijalankannya dan (seperti dengan monad reguler *
) jenis apa value
yang dihasilkan komputasi.
Bit dan potongan biasa adalah *
-wise seperti monad dan state
-wise seperti bermain domino.
ireturn :: a -> m i i a -- returning a pure value preserves state
ibind :: m i j a -> -- we can go from i to j and get an a, thence
(a -> m j k b) -- we can go from j to k and get a b, therefore
-> m i k b -- we can indeed go from i to k and get a b
Gagasan tentang "panah Kleisli" (fungsi yang menghasilkan komputasi) yang dihasilkan adalah
a -> m i j b -- values a in, b out; state transition i to j
dan kami mendapatkan komposisi
icomp :: IxMonad m => (b -> m j k c) -> (a -> m i j b) -> a -> m i k c
icomp f g = \ a -> ibind (g a) f
dan, seperti biasa, hukum memastikan hal itu ireturn
dan icomp
memberi kita kategori
ireturn `icomp` g = g
f `icomp` ireturn = f
(f `icomp` g) `icomp` h = f `icomp` (g `icomp` h)
atau, dalam komedi palsu C / Java / apapun,
g(); skip = g()
skip; f() = f()
{g(); h()}; f() = h(); {g(); f()}
Mengapa mengganggu? Untuk mencontohkan "aturan" interaksi. Misalnya, Anda tidak dapat mengeluarkan dvd jika tidak ada di drive, dan Anda tidak dapat memasukkan dvd ke dalam drive jika sudah ada di dalamnya. Begitu
data DVDDrive :: Bool -> Bool -> * -> * where -- Bool is "drive full?"
DReturn :: a -> DVDDrive i i a
DInsert :: DVD -> -- you have a DVD
DVDDrive True k a -> -- you know how to continue full
DVDDrive False k a -- so you can insert from empty
DEject :: (DVD -> -- once you receive a DVD
DVDDrive False k a) -> -- you know how to continue empty
DVDDrive True k a -- so you can eject when full
instance IxMonad DVDDrive where -- put these methods where they need to go
ireturn = DReturn -- so this goes somewhere else
ibind (DReturn a) k = k a
ibind (DInsert dvd j) k = DInsert dvd (ibind j k)
ibind (DEject j) k = DEject j $ \ dvd -> ibind (j dvd) k
Dengan ini, kita bisa mendefinisikan perintah "primitif"
dInsert :: DVD -> DVDDrive False True ()
dInsert dvd = DInsert dvd $ DReturn ()
dEject :: DVDrive True False DVD
dEject = DEject $ \ dvd -> DReturn dvd
dari mana orang lain berkumpul dengan ireturn
dan ibind
. Sekarang, saya bisa menulis (meminjam- do
notasi)
discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dvd' <- dEject; dInsert dvd ; ireturn dvd'
tapi bukan yang tidak mungkin secara fisik
discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dInsert dvd; dEject -- ouch!
Alternatifnya, seseorang dapat mendefinisikan perintah primitifnya secara langsung
data DVDCommand :: Bool -> Bool -> * -> * where
InsertC :: DVD -> DVDCommand False True ()
EjectC :: DVDCommand True False DVD
dan kemudian membuat contoh template generik
data CommandIxMonad :: (state -> state -> * -> *) ->
state -> state -> * -> * where
CReturn :: a -> CommandIxMonad c i i a
(:?) :: c i j a -> (a -> CommandIxMonad c j k b) ->
CommandIxMonad c i k b
instance IxMonad (CommandIxMonad c) where
ireturn = CReturn
ibind (CReturn a) k = k a
ibind (c :? j) k = c :? \ a -> ibind (j a) k
Akibatnya, kita telah mengatakan apa itu panah Kleisli primitif (apa itu "domino"), kemudian membangun gagasan yang sesuai tentang "urutan komputasi" di atasnya.
Perhatikan bahwa untuk setiap monad yang diindeks m
, "tanpa perubahan diagonal" m i i
adalah monad, tetapi secara umum m i j
tidak. Selain itu, nilai tidak diindeks tetapi perhitungan diindeks, jadi monad yang diindeks bukan hanya gagasan biasa dari monad yang dipakai untuk beberapa kategori lain.
Sekarang, lihat kembali jenis panah Kleisli
a -> m i j b
Kami tahu kami harus berada dalam keadaan i
untuk memulai, dan kami memperkirakan bahwa kelanjutan apa pun akan dimulai dari keadaan j
. Kami tahu banyak tentang sistem ini! Ini bukan operasi yang berisiko! Saat kita memasukkan dvd ke dalam drive, itu masuk! Drive dvd tidak dapat menentukan status setelah setiap perintah.
Tapi itu tidak benar secara umum, saat berinteraksi dengan dunia. Terkadang Anda mungkin perlu memberikan kendali dan membiarkan dunia melakukan apa yang disukainya. Misalnya, jika Anda adalah server, Anda mungkin menawarkan pilihan kepada klien, dan status sesi Anda akan bergantung pada apa yang mereka pilih. Operasi "pilihan penawaran" server tidak menentukan status yang dihasilkan, tetapi server harus dapat melanjutkannya. Ini bukan "perintah primitif" dalam pengertian di atas, jadi monad yang diindeks bukanlah alat yang baik untuk membuat model skenario yang tidak dapat diprediksi .
Alat apa yang lebih baik?
type f :-> g = forall state. f state -> g state
class MonadIx (m :: (state -> *) -> (state -> *)) where
returnIx :: x :-> m x
flipBindIx :: (a :-> m b) -> (m a :-> m b) -- tidier than bindIx
Biskuit menakutkan? Tidak juga, karena dua alasan. Satu, lebih terlihat seperti apa itu monad, karena itu adalah monad, tetapi lebih (state -> *)
daripada *
. Kedua, jika Anda melihat jenis panah Kleisli,
a :-> m b = forall state. a state -> m b state
Anda mendapatkan jenis perhitungan dengan prasyarat a
dan pascakondisi b
, seperti di Good Old Hoare Logic. Pernyataan dalam logika program membutuhkan waktu kurang dari setengah abad untuk melintasi korespondensi Curry-Howard dan menjadi tipe Haskell. Jenis returnIx
kata "Anda dapat mencapai kondisi akhir apa pun yang berlaku, hanya dengan tidak melakukan apa pun", yang merupakan aturan Logika Hoare untuk "lewati". Komposisi yang sesuai adalah aturan Hoare Logic untuk ";".
Mari selesaikan dengan melihat jenisnya bindIx
, masukkan semua bilangannya.
bindIx :: forall i. m a i -> (forall j. a j -> m b j) -> m b i
Ini forall
memiliki polaritas yang berlawanan. Kami memilih status awal i
, dan komputasi yang dapat dimulai pada i
, dengan kondisi pos a
. Dunia memilih negara perantara apa pun yang j
disukainya, tetapi dunia harus memberi kita bukti bahwa kondisi pasca b
berlaku, dan dari keadaan seperti itu, kita dapat terus b
mempertahankannya. Jadi, secara berurutan, kita bisa mencapai kondisi b
dari negara i
. Dengan melepaskan pegangan kita pada status "setelah", kita dapat membuat model komputasi yang tidak dapat diprediksi .
Keduanya IxMonad
dan MonadIx
berguna. Kedua model validitas komputasi interaktif sehubungan dengan perubahan keadaan, dapat diprediksi dan tidak dapat diprediksi, masing-masing. Prediktabilitas sangat berharga jika Anda bisa mendapatkannya, tetapi ketidakpastian terkadang merupakan fakta kehidupan. Mudah-mudahan, jawaban ini memberikan beberapa indikasi tentang apa itu indexed monads, memprediksi kapan mereka mulai berguna dan kapan berhenti.
True
/False
values sebagai argumen tipeDVDDrive
? Apakah itu beberapa ekstensi, atau apakah boolean benar-benar mengetik di sini?