Saya datang ke posting ini dengan cara lebih memahami kesimpulan kutipan terkenal dari Teori Kategori Mac Lane Untuk Matematika yang Bekerja .
Dalam menggambarkan apa itu sesuatu, seringkali sama bermanfaatnya untuk menggambarkan apa itu bukan.
Fakta bahwa Mac Lane menggunakan deskripsi untuk menggambarkan Monad, orang mungkin menyiratkan bahwa itu menggambarkan sesuatu yang unik untuk monad. Tetap bersamaku. Untuk mengembangkan pemahaman yang lebih luas tentang pernyataan itu, saya percaya perlu dijelaskan bahwa dia tidak menggambarkan sesuatu yang unik untuk monad; pernyataan itu sama-sama menggambarkan antara Applicative dan Arrows. Untuk alasan yang sama kita dapat memiliki dua monoids pada Int (Jumlah dan Produk), kita dapat memiliki beberapa monoids pada X dalam kategori endofunctors. Tetapi ada lebih banyak kesamaan.
Baik Monad maupun Aplikatif memenuhi kriteria:
Pernyataan ini menggunakan "Kategori ..." Ini mendefinisikan ruang lingkup pernyataan. Sebagai contoh, Kategori Functor menggambarkan ruang lingkup f * -> g *
, yaitu Any functor -> Any functor
, misalnya, Tree * -> List *
atau Tree * -> Tree *
.
Apa yang tidak disebutkan dalam pernyataan Kategoris menjelaskan di mana segala sesuatu dan segala sesuatu diizinkan .
Dalam hal ini, di dalam functors, * -> *
alias a -> b
tidak ditentukan artinya Anything -> Anything including Anything else
. Ketika imajinasi saya melompat ke Int -> String, itu juga termasuk Integer -> Maybe Int
, atau bahkan di Maybe Double -> Either String Int
mana a :: Maybe Double; b :: Either String Int
.
Jadi pernyataan itu muncul bersama sebagai berikut:
- ruang lingkup functor
:: f a -> g b
(yaitu, semua tipe parameter untuk semua tipe parameter)
- endo + functor
:: f a -> f b
(yaitu, salah satu jenis parameter untuk jenis parameter yang sama) ... berkata berbeda,
- monoid dalam kategori endofunctor
Jadi, di mana kekuatan konstruksi ini? Untuk menghargai dinamika penuh, saya perlu melihat bahwa gambar tipikal monoid (objek tunggal dengan apa yang tampak seperti panah identitas :: single object -> single object
), gagal menggambarkan bahwa saya diizinkan menggunakan panah yang diparameterisasi dengan sejumlah nilai monoid, dari objek satu jenis yang diizinkan dalam Monoid. Definisi tanda panah endo, ~ identitas tentang kesetaraan mengabaikan nilai tipe functor dan baik tipe maupun nilai dari lapisan "payload" yang paling dalam. Dengan demikian, kesetaraan kembali true
dalam situasi apa pun di mana jenis fungsi cocok (misalnya, Nothing -> Just * -> Nothing
setara dengan Just * -> Just * -> Just *
karena keduanya Maybe -> Maybe -> Maybe
).
Bilah samping: ~ luar adalah konseptual, tetapi merupakan simbol paling kiri di f a
. Ini juga menggambarkan apa yang "Haskell" baca pertama (gambaran besar); jadi Ketik adalah "luar" dalam kaitannya dengan Nilai Jenis. Hubungan antar lapisan (rangkaian referensi) dalam pemrograman tidak mudah dihubungkan dalam Kategori. Kategori Set digunakan untuk menggambarkan Jenis (Int, String, Mungkin Int dll.) Yang mencakup Kategori Functor (Tipe parameter). Rantai referensi: Jenis Functor, nilai-nilai Functor (elemen-elemen dari set Functor itu, misalnya, Tidak Ada, Hanya), dan pada gilirannya, segala sesuatu yang ditunjuk oleh masing-masing nilai functor. Dalam Kategori hubungan dijelaskan secara berbeda, misalnya, return :: a -> m a
dianggap sebagai transformasi alami dari satu Functor ke Functor lain, berbeda dari apa pun yang disebutkan sejauh ini.
Kembali ke utas utama, secara keseluruhan, untuk setiap produk tensor yang ditentukan dan nilai netral, pernyataan tersebut akhirnya menggambarkan konstruksi komputasi yang sangat kuat yang lahir dari struktur paradoksnya:
- di luar itu muncul sebagai objek tunggal (misalnya,
:: List
); statis
- tetapi di dalam, memungkinkan banyak dinamika
- sejumlah nilai dari jenis yang sama (misalnya, Kosong | ~ NonEmpty) sebagai pakan ternak untuk fungsi dari setiap arity. Produk tensor akan mengurangi sejumlah input ke nilai tunggal ... untuk lapisan eksternal (~
fold
yang tidak mengatakan apa-apa tentang payload)
- kisaran tak terbatas dari kedua jenis dan nilai untuk lapisan paling dalam
Di Haskell, memperjelas penerapan pernyataan itu penting. Kekuatan dan fleksibilitas dari konstruk ini, sama sekali tidak ada hubungannya dengan monad per se . Dengan kata lain, konstruk tidak bergantung pada apa yang membuat monad unik.
Ketika mencoba mencari tahu apakah akan membangun kode dengan konteks bersama untuk mendukung perhitungan yang saling bergantung satu sama lain, dibandingkan perhitungan yang dapat dijalankan secara paralel, pernyataan terkenal ini, dengan sebanyak yang dijelaskan, tidak kontras antara pilihan Applicative, Arrows and Monads, tetapi lebih merupakan deskripsi dari seberapa banyak mereka sama. Untuk keputusan yang diambil, pernyataan itu bisa diperdebatkan.
Ini sering disalahpahami. Pernyataan itu selanjutnya menggambarkan join :: m (m a) -> m a
sebagai produk tensor untuk endofunctor monoid. Namun, itu tidak mengartikulasikan bagaimana, dalam konteks pernyataan ini, (<*>)
juga bisa dipilih. Ini benar-benar adalah contoh enam / setengah lusin. Logika untuk menggabungkan nilai-nilai persis sama; input yang sama menghasilkan output yang sama dari masing-masing (tidak seperti jumlah dan monoid Produk untuk Int karena mereka menghasilkan hasil yang berbeda ketika menggabungkan Ints).
Jadi, untuk rekap: Monoide dalam kategori endofunctors menggambarkan:
~t :: m * -> m * -> m *
and a neutral value for m *
(<*>)
dan (>>=)
keduanya memberikan akses simultan ke dua m
nilai untuk menghitung nilai pengembalian tunggal. Logika yang digunakan untuk menghitung nilai kembali persis sama. Jika bukan karena bentuk yang berbeda dari fungsi yang mereka parameterkan ( f :: a -> b
versus k :: a -> m b
) dan posisi parameter dengan tipe pengembalian yang sama dari perhitungan (yaitu, a -> b -> b
dibandingkan b -> a -> b
untuk masing-masing), saya curiga kita bisa parameter logika monoid, produk tensor, untuk digunakan kembali dalam kedua definisi. Sebagai latihan untuk menyampaikan maksud, cobalah dan terapkan ~t
, dan Anda berakhir dengan (<*>)
dan (>>=)
tergantung pada bagaimana Anda memutuskan untuk mendefinisikannya forall a b
.
Jika poin terakhir saya paling tidak secara konsepsi benar, maka itu menjelaskan perbedaan komputasi yang tepat, dan hanya antara Applicative dan Monad: fungsi yang mereka parameterkan. Dengan kata lain, perbedaannya adalah eksternal untuk implementasi kelas tipe ini.
Kesimpulannya, dalam pengalaman saya sendiri, kutipan terkenal dari Mac Lane memberikan meme "goto" yang luar biasa, sebuah patokan bagi saya untuk referensi sambil menavigasi jalan saya melalui Kategori untuk lebih memahami idiom yang digunakan dalam Haskell. Ini berhasil menangkap ruang lingkup kapasitas komputasi yang kuat yang dapat diakses dengan luar biasa di Haskell.
Namun, ada ironi dalam cara saya pertama kali salah memahami penerapan pernyataan di luar monad, dan apa yang saya harap sampaikan di sini. Segala sesuatu yang dijelaskannya ternyata mirip dengan yang berlaku antara Applicative dan Monads (dan Arrows antara lain). Apa yang tidak dikatakannya adalah perbedaan kecil tapi bermanfaat di antara mereka.
- E