Monad hanya monoid dalam kategori endofunctor, apa masalahnya?

723

Siapa yang pertama kali mengatakan yang berikut?

Monad hanya monoid dalam kategori endofunctor, apa masalahnya?

Dan pada catatan yang kurang penting, apakah ini benar dan jika demikian dapatkah Anda memberikan penjelasan (mudah-mudahan yang dapat dipahami oleh seseorang yang tidak memiliki banyak pengalaman Haskell)?

Roman A. Taycher
sumber
14
Lihat "Kategori untuk Matematika yang Bekerja"
Don Stewart
19
Anda tidak perlu memahami ini untuk menggunakan monad di Haskell. Dari sudut pandang praktis, mereka hanyalah cara pintar untuk melewati "keadaan" melalui beberapa pipa bawah tanah.
starblue
1
Saya juga ingin menambahkan posting blog yang luar biasa ini di sini: stephendiehl.com/posts/monads.html Tidak secara langsung menjawab pertanyaan, tetapi menurut saya Stephen melakukan pekerjaan yang luar biasa dengan mengikat kategori dan monad di Haskell bersama-sama. Jika Anda sudah membaca jawaban di atas - ini akan membantu menyatukan dua cara memandang ini.
Ben Ford
3
Lebih tepatnya "Untuk kategori C apa pun, kategori [C, C] dari endofunctornya memiliki struktur monoid yang disebabkan oleh komposisi. Objek monoid dalam [C, C] adalah monad pada C." - dari en.wikipedia.org/wiki/Monoid_%28category_theory%29. Lihat en.wikipedia.org/wiki/Monad_%28category_theory%29 untuk definisi monad dalam teori kategori.
1
@Dmitry functor adalah fungsi antara kategori, dengan beberapa kendala untuk berperilaku baik. Endofunctor pada kategori C hanyalah functor dari C ke dirinya sendiri. Data.Functor adalah typeclass untuk endofunctors pada kategori Hask . Karena kategori terdiri dari objek dan morfisme, seorang functor perlu memetakan keduanya. Sebagai contoh f Data.Functor, peta pada objek (tipe haskell) adalah f itu sendiri dan peta pada morfisme (fungsi haskell) adalah fmap.
Matthijs

Jawaban:

796

Ungkapan khusus itu adalah oleh James Iry, dari Briefnya yang sangat menghibur , Tidak Lengkap, dan Paling Salah Sejarah Bahasa Pemrograman , di mana ia secara fiksi menghubungkannya dengan Philip Wadler.

Kutipan asli dari Saunders Mac Lane di Categories for the Working Mathematician , salah satu teks dasar Category Theory. Ini dia dalam konteks , yang mungkin merupakan tempat terbaik untuk mempelajari dengan tepat apa artinya.

Tapi, saya akan coba. Kalimat aslinya adalah ini:

Semua mengatakan, monad dalam X hanyalah monoid dalam kategori endofunctor X, dengan produk × digantikan oleh komposisi endofunctor dan unit yang ditetapkan oleh endofunctor identitas.

X di sini adalah kategori. Endofunctors adalah functors dari kategori ke dirinya sendiri (yang biasanya semua Functor sejauh menyangkut programmer fungsional, karena mereka kebanyakan berurusan dengan hanya satu kategori; kategori jenis - tapi saya ngelantur). Tetapi Anda bisa membayangkan kategori lain yang merupakan kategori "endofunctors on X ". Ini adalah kategori di mana objek adalah endofunctor dan morfisme adalah transformasi alami.

Dan di antara petugas akhir itu, beberapa di antaranya mungkin adalah monad. Yang mana yang merupakan monad? Tepatnya yang monoid dalam arti tertentu. Alih-alih menguraikan pemetaan yang tepat dari monad ke monoid (karena Mac Lane melakukan itu jauh lebih baik daripada yang saya harapkan), saya hanya akan menempatkan definisi masing-masing berdampingan dan membiarkan Anda membandingkan:

Monoid adalah ...

  • Satu set, S
  • Suatu operasi, •: S × S → S
  • Unsur S , e: 1 → S

... memenuhi hukum ini:

  • (a • b) • c = a • (b • c) , untuk semua a , b dan c di S
  • e • a = a • e = a , untuk semua a di S

Monad adalah ...

  • Endofunctor, T: X → X (dalam Haskell, jenis konstruktor * -> *dengan aFunctor instance)
  • Transformasi alami, μ: T × T → T , di mana × berarti komposisi functor ( μ dikenal sebagai joindalam Haskell)
  • Transformasi alami, η: I → T , di mana saya adalah endofunctor identitas pada X ( η dikenal sebagai returndi Haskell)

... memenuhi hukum ini:

  • μ ∘ Tμ = μ ∘ μT
  • μ ∘ Tη = μ ∘ ηT = 1 (transformasi alami identitas)

Dengan sedikit menyipit Anda mungkin dapat melihat bahwa kedua definisi ini adalah contoh dari konsep abstrak yang sama .

Tom Crockett
sumber
21
terima kasih atas penjelasannya dan terima kasih untuk artikel Singkat, Tidak Lengkap, dan Paling Salah Sejarah Bahasa Pemrograman. Saya pikir itu mungkin dari sana. Benar-benar salah satu bagian terbesar dari humor pemrograman.
Roman A. Taycher
6
@ Jonathan: Dalam formulasi klasik monoid, × berarti produk kartesian set. Anda dapat membaca lebih lanjut tentang itu di sini: en.wikipedia.org/wiki/Cartesian_product , tetapi ide dasarnya adalah bahwa unsur S × T adalah pasangan (s, t) , di mana s ∈ S dan t ∈ T . Jadi tanda tangan dari produk monoid •: S × S -> S dalam konteks ini berarti fungsi yang mengambil 2 elemen S sebagai input dan menghasilkan elemen S lainnya sebagai output.
Tom Crockett
12
@ TahirHassan - Dalam generalitas teori kategori, kita berurusan dengan "objek" buram alih-alih set, dan dengan demikian tidak ada gagasan apriori "elemen". Tetapi jika Anda berpikir tentang kategori Set di mana objek ditetapkan dan panah adalah fungsi, elemen dari setiap himpunan S berada dalam korespondensi satu-ke-satu dengan fungsi dari setiap elemen satu set ke S. Yaitu, untuk elemen e dari S , ada persis satu fungsi f: 1 -> S , di mana 1 adalah elemen satu-set ... (lanjutan)
Tom Crockett
12
@TahirHassan 1-elemen set sendiri adalah spesialisasi dari kategori yang lebih umum-teori tentang "objek terminal": objek terminal adalah objek apa pun dari kategori yang hanya ada satu panah dari objek lain ke objek tersebut (Anda dapat memeriksa apakah ini berlaku untuk set 1-elemen di Set ). Dalam kategori teori, objek terminal secara sederhana disebut sebagai 1 ; mereka unik hingga isomorfisme sehingga tidak ada gunanya membedakan mereka. Jadi sekarang kita memiliki deskripsi kategori-teoretis murni "elemen S " untuk S apa pun : mereka hanya panah dari 1 ke S !
Tom Crockett
7
@TahirHassan - Untuk memasukkan ini dalam istilah Haskell, pikirkan fakta bahwa jika Sadalah tipe, yang dapat Anda lakukan ketika menulis fungsi f :: () -> Sadalah memilih beberapa istilah tertentu dari tipe S("elemen" itu, jika Anda mau) dan kembali itu ... Anda tidak diberi informasi nyata dengan argumennya, jadi tidak ada cara untuk memvariasikan perilaku fungsi. Jadi fharus merupakan fungsi konstan yang hanya mengembalikan hal yang sama setiap waktu. ()("Unit") adalah objek terminal dari kategori Hask , dan bukan kebetulan bahwa ada tepat 1 (non-divergen) nilai yang menghuninya.
Tom Crockett
532

Secara intuitif, saya berpikir bahwa apa yang dikatakan oleh kosa kata matematika mewah adalah:

Monoid

Sebuah monoid adalah satu set objek, dan metode menggabungkan mereka. Monoids terkenal adalah:

  • angka yang dapat Anda tambahkan
  • daftar yang dapat Anda gabungkan
  • membuat Anda bisa bersatu

Ada juga contoh yang lebih kompleks.

Lebih lanjut, setiap monoid memiliki identitas , yaitu elemen "tidak-op" yang tidak memiliki efek ketika Anda menggabungkannya dengan sesuatu yang lain:

  • 0 + 7 == 7 + 0 == 7
  • [] ++ [1,2,3] == [1,2,3] ++ [] == [1,2,3]
  • {} union {apple} == {apple} union {} == {apple}

Akhirnya, monoid harus asosiatif . (Anda dapat mengurangi serangkaian panjang kombinasi yang Anda inginkan, selama Anda tidak mengubah urutan objek dari kiri ke kanan). Penambahan adalah OK ((5 + 3) +1 == 5+ (3+ 1)), tetapi pengurangan bukan ((5-3) -1! = 5- (3-1)).

Monad

Sekarang, mari kita pertimbangkan jenis set khusus dan cara khusus untuk menggabungkan objek.

Benda

Misalkan set Anda berisi objek jenis khusus: fungsi . Dan fungsi-fungsi ini memiliki tanda tangan yang menarik: Mereka tidak membawa angka ke angka atau string ke string. Sebaliknya, setiap fungsi membawa nomor ke daftar angka dalam proses dua langkah.

  1. Hitung 0 atau lebih hasil
  2. Gabungkan hasil-hasil itu dengan satu jawaban.

Contoh:

  • 1 -> [1] (hanya membungkus input)
  • 1 -> [] (membuang input, bungkus ketiadaan dalam daftar)
  • 1 -> [2] (tambahkan 1 ke input, dan bungkus hasilnya)
  • 3 -> [4, 6] (tambahkan 1 ke input, dan gandakan input dengan 2, dan bungkus beberapa hasil )

Menggabungkan Objek

Juga, cara kami menggabungkan fungsi adalah spesial. Cara sederhana untuk menggabungkan fungsi adalah komposisi : Mari kita ambil contoh di atas, dan susun setiap fungsi dengan sendirinya:

  • 1 -> [1] -> [[1]] (bungkus input, dua kali)
  • 1 -> [] -> [] (buang input, bungkus ketiadaan dalam daftar, dua kali)
  • 1 -> [2] -> [UH-OH! ] (kami tidak dapat "menambahkan 1" ke daftar! ")
  • 3 -> [4, 6] -> [UH-OH! ] (kami tidak dapat menambahkan 1 daftar!)

Tanpa terlalu banyak ke dalam teori tipe, intinya adalah bahwa Anda dapat menggabungkan dua bilangan bulat untuk mendapatkan bilangan bulat, tetapi Anda tidak selalu dapat membuat dua fungsi dan mendapatkan fungsi dengan tipe yang sama. (Fungsi dengan tipe a -> a akan menulis, tetapi a-> [a] tidak.)

Jadi, mari kita definisikan cara berbeda untuk menggabungkan fungsi. Saat kami menggabungkan dua fungsi ini, kami tidak ingin "membungkus ganda" hasilnya.

Inilah yang kami lakukan. Saat kami ingin menggabungkan dua fungsi F dan G, kami mengikuti proses ini (disebut binding ):

  1. Hitung "hasil" dari F tetapi jangan gabungkan.
  2. Hitung hasil dari menerapkan G ke masing-masing hasil F secara terpisah, menghasilkan koleksi koleksi hasil.
  3. Ratakan koleksi 2 tingkat dan gabungkan semua hasil.

Kembali ke contoh kita, mari kita gabungkan (ikat) fungsi dengan dirinya sendiri menggunakan cara baru fungsi "mengikat" ini:

  • 1 -> [1] -> [1] (bungkus input, dua kali)
  • 1 -> [] -> [] (buang input, bungkus ketiadaan dalam daftar, dua kali)
  • 1 -> [2] -> [3] (tambahkan 1, lalu tambahkan 1 lagi, dan bungkus hasilnya.)
  • 3 -> [4,6] -> [5,8,7,12] (tambahkan 1 ke input, dan juga gandakan input dengan 2, pertahankan kedua hasil, lalu lakukan semuanya lagi untuk kedua hasil, lalu bungkus akhir hasil dalam daftar.)

Cara menggabungkan fungsi yang lebih canggih ini adalah asosiatif (mengikuti dari bagaimana komposisi fungsi asosiatif ketika Anda tidak melakukan hal-hal pembungkus yang bagus).

Mengikat semuanya,

  • monad adalah struktur yang mendefinisikan cara untuk menggabungkan (hasil) fungsi,
  • analog dengan bagaimana monoid adalah struktur yang mendefinisikan cara untuk menggabungkan objek,
  • dimana metode kombinasi adalah asosiatif,
  • dan di mana ada 'No-op' khusus yang dapat dikombinasikan dengan sesuatu untuk menghasilkan sesuatu yang tidak berubah.

Catatan

Ada banyak cara untuk "membungkus" hasil. Anda dapat membuat daftar, atau set, atau membuang semua kecuali hasil pertama sambil mencatat jika tidak ada hasil, lampirkan sespan keadaan, cetak pesan log, dll, dll.

Saya telah bermain agak longgar dengan definisi dengan harapan mendapatkan ide penting secara intuitif.

Saya telah menyederhanakan banyak hal dengan menegaskan bahwa monad kami beroperasi pada fungsi tipe a -> [a] . Bahkan, monads bekerja pada fungsi tipe a -> mb , tetapi generalisasi adalah semacam detail teknis yang bukan wawasan utama.

misterbee
sumber
22
Ini adalah penjelasan yang bagus tentang bagaimana setiap monad membentuk suatu kategori ( kategori Kleisli adalah yang Anda tunjukkan - ada juga kategori Eilenberg-Moore). Namun karena fakta bahwa Anda tidak dapat membuat setiap dua panah Kleisli a -> [b]dan c -> [d](Anda hanya dapat melakukan ini jika b= c), ini tidak cukup menggambarkan monoid a. Ini sebenarnya operasi perataan yang Anda gambarkan, daripada komposisi fungsi, yang merupakan "operator monoid".
Tom Crockett
6
Memang, jika Anda membatasi monad hanya untuk satu jenis, yaitu jika Anda hanya memperbolehkan panah Kleisli dari formulir a -> [a], ini akan menjadi monoid (karena Anda akan mengurangi kategori Kleisli menjadi satu objek tunggal, dan kategori apa pun dari satu objek saja menurut definisi adalah monoid!), tetapi itu tidak akan menangkap keseluruhan umum monad.
Tom Crockett
5
Pada catatan terakhir, perlu diingat, bahwa a -> [a] hanya a -> [] a. ([] juga hanya tipe konstruktor.) Dan itu tidak hanya dapat dilihat sebagai -> mb, tetapi [] memang merupakan turunan dari kelas Monad.
Evi1M4chine
8
Ini adalah penjelasan terbaik dan paling grokkable tentang monad dan latar belakang matematis dari monoids yang saya temui dalam beberapa minggu. Ini adalah apa yang harus dicetak di setiap buku Haskell ketika datang ke monad, tangan ke bawah. UPVOTE! Mungkin lebih jauh mendapatkan potongan informasi, bahwa monad diwujudkan sebagai contoh typeclass membungkus apa pun yang dimasukkan ke dalam haskell, ke dalam pos. (Setidaknya begitulah cara saya memahaminya sekarang. Koreksi saya jika saya salah. Lihat haskell.org/haskellwiki/What_a_Monad_is_not )
sjas
1
Ini luar biasa - ini satu-satunya penjelasan yang saya cukup pahami untuk bisa menjelaskannya kepada orang lain ... Tapi saya masih tidak mengerti mengapa ini adalah cara yang berharga untuk memikirkan apa pun. :(
Adam Barnes
84

Pertama, ekstensi dan pustaka yang akan kita gunakan:

{-# LANGUAGE RankNTypes, TypeOperators #-}

import Control.Monad (join)

Dari jumlah tersebut, RankNTypesadalah satu-satunya yang mutlak penting untuk di bawah ini. Saya pernah menulis penjelasan RankNTypesbahwa beberapa orang tampaknya bermanfaat , jadi saya akan merujuknya.

Mengutip jawaban Tom Crockett yang luar biasa , kami memiliki:

Monad adalah ...

  • Seorang endofunctor, T: X -> X
  • Transformasi alami, μ: T × T -> T , di mana × berarti komposisi functor
  • Transformasi alami, η: I -> T , di mana saya adalah endofunctor identitas pada X

... memenuhi hukum ini:

  • μ (μ (T × T) × T)) = μ (T × μ (T × T))
  • μ (η (T)) = T = μ (T (η))

Bagaimana kita menerjemahkan ini ke kode Haskell? Baiklah, mari kita mulai dengan gagasan transformasi alami :

-- | A natural transformations between two 'Functor' instances.  Law:
--
-- > fmap f . eta g == eta g . fmap f
--
-- Neat fact: the type system actually guarantees this law.
--
newtype f :-> g =
    Natural { eta :: forall x. f x -> g x }

Jenis bentuk f :-> ganalog dengan tipe fungsi, tetapi alih-alih menganggapnya sebagai fungsi antara dua jenis (jenis *), anggap itu sebagai morfisme antara dua fungsi (masing-masing jenis * -> *). Contoh:

listToMaybe :: [] :-> Maybe
listToMaybe = Natural go
    where go [] = Nothing
          go (x:_) = Just x

maybeToList :: Maybe :-> []
maybeToList = Natural go
    where go Nothing = []
          go (Just x) = [x]

reverse' :: [] :-> []
reverse' = Natural reverse

Pada dasarnya, di Haskell, transformasi alami adalah fungsi dari beberapa tipe f xke tipe lain g xsehingga xvariabel tipe "tidak dapat diakses" oleh pemanggil. Jadi misalnya,sort :: Ord a => [a] -> [a] tidak dapat dibuat menjadi transformasi alami, karena itu "pilih-pilih" tentang jenis yang kita instantiate a. Satu cara intuitif yang sering saya gunakan untuk memikirkan ini adalah sebagai berikut:

  • Functor adalah cara beroperasi pada konten sesuatu tanpa menyentuh struktur .
  • Transformasi alami adalah cara beroperasi pada struktur sesuatu tanpa menyentuh atau melihat konten .

Sekarang, dengan itu, mari kita bahas pasal-pasal definisi tersebut.

Klausa pertama adalah "seorang endofunctor, T: X -> X. " Nah, setiap orang Functordi Haskell adalah endofunctor dalam apa yang orang-orang sebut "kategori Hask," yang objeknya adalah tipe Haskell (sejenis *) dan yang morfismenya adalah fungsi Haskell. Ini kedengarannya seperti pernyataan yang rumit, tapi itu sebenarnya sangat sepele. Semua itu berarti bahwa a Functor f :: * -> *memberi Anda cara membangun tipe f a :: *untuk apa saja a :: *dan fungsi fmap f :: f a -> f bdari apa saja f :: a -> b, dan bahwa ini mematuhi hukum functor.

Klausa kedua: Identityfunctor di Haskell (yang datang dengan Platform, jadi Anda bisa mengimpornya) didefinisikan dengan cara ini:

newtype Identity a = Identity { runIdentity :: a }

instance Functor Identity where
    fmap f (Identity a) = Identity (f a)

Jadi transformasi alam η: I -> T dari definisi Tom Crockett dapat ditulis dengan cara ini untuk setiap Monadmisalnya t:

return' :: Monad t => Identity :-> t
return' = Natural (return . runIdentity)

Klausa ketiga: Komposisi dua functors di Haskell dapat didefinisikan dengan cara ini (yang juga dilengkapi dengan Platform):

newtype Compose f g a = Compose { getCompose :: f (g a) }

-- | The composition of two 'Functor's is also a 'Functor'.
instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose fga) = Compose (fmap (fmap f) fga)

Jadi transformasi alami μ: T × T -> T dari definisi Tom Crockett dapat ditulis seperti ini:

join' :: Monad t => Compose t t :-> t
join' = Natural (join . getCompose)

Pernyataan bahwa ini adalah monoid dalam kategori endofunctor berarti bahwa Compose(sebagian diterapkan hanya pada dua parameter pertama) adalah asosiatif, dan itu Identityadalah elemen identitasnya. Yaitu, bahwa isomorfisma berikut ini berlaku:

  • Compose f (Compose g h) ~= Compose (Compose f g) h
  • Compose f Identity ~= f
  • Compose Identity g ~= g

Ini sangat mudah dibuktikan karena Composedan Identitykeduanya didefinisikan sebagai newtype, dan Laporan Haskell mendefinisikan semantik newtypesebagai isomorfisme antara jenis yang didefinisikan dan jenis argumen ke newtypekonstruktor data. Jadi misalnya, mari kita buktikan Compose f Identity ~= f:

Compose f Identity a
    ~= f (Identity a)                 -- newtype Compose f g a = Compose (f (g a))
    ~= f a                            -- newtype Identity a = Identity a
Q.E.D.
Luis Casillas
sumber
Pada Naturaltipe baru, saya tidak tahu apa yang dilakukan (Functor f, Functor g)kendala. Bisakah Anda jelaskan?
dfeuer
@ PDFeuer Ini tidak benar-benar melakukan sesuatu yang penting.
Luis Casillas
1
@LuisCasillas Saya telah menghapus Functorkendala itu karena tampaknya tidak perlu. Jika Anda tidak setuju maka silakan menambahkannya kembali.
Lambda Fairy
Bisakah Anda menguraikan apa artinya secara formal untuk produk functors yang akan diambil sebagai komposisi? Secara khusus, apa morfisme proyeksi untuk komposisi functor? Dugaan saya adalah bahwa produk hanya ditentukan untuk F functor terhadap dirinya sendiri, F x F dan hanya ketika joindidefinisikan. Dan itu joinadalah morfisme proyeksi. Tapi saya tidak yakin.
tksfz
6

Catatan: Tidak, ini tidak benar. Pada titik tertentu ada komentar pada jawaban ini dari Dan Piponi sendiri yang mengatakan bahwa sebab dan akibat di sini adalah kebalikannya, bahwa ia menulis artikelnya sebagai tanggapan atas sindiran James Iry. Tetapi tampaknya telah dihapus, mungkin oleh beberapa rapi lebih kompulsif.

Di bawah ini adalah jawaban asli saya.


Sangat mungkin bahwa Iry telah membaca From Monoids to Monads , sebuah pos di mana Dan Piponi (sigfpe) memperoleh monads dari monoids di Haskell, dengan banyak diskusi tentang teori kategori dan menyebutkan secara eksplisit "kategori endofunctors on Hask ". Dalam kasus apa pun, siapa pun yang bertanya-tanya apa artinya sebuah monad menjadi monoid dalam kategori endofunctor mungkin mendapat manfaat dari membaca derivasi ini.

hobbs
sumber
1
"Mungkin oleh orang yang lebih pintar kompulsif" - atau, seperti yang kita suka lihat di situs ini, seorang moderator :-).
halfer
6

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:

  • endo => sembarang panah, atau morfisme yang dimulai dan berakhir di tempat yang sama
  • functor => sembarang panah, atau morfisme antara dua Kategori

    (mis. dalam sehari-hari Tree a -> List b, tetapi dalam Kategori Tree -> List)

  • monoid => objek tunggal; yaitu, tipe tunggal, tetapi dalam konteks ini, hanya dalam hal lapisan eksternal; jadi, kita tidak bisa Tree -> List, hanya List -> List.

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 -> btidak 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 Intmana 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 truedalam situasi apa pun di mana jenis fungsi cocok (misalnya, Nothing -> Just * -> Nothingsetara 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 adianggap 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 (~ foldyang 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 asebagai 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 mnilai 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 -> bversus k :: a -> m b) dan posisi parameter dengan tipe pengembalian yang sama dari perhitungan (yaitu, a -> b -> bdibandingkan b -> a -> buntuk 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

Edmund's Echo
sumber
5

Jawaban di sini melakukan pekerjaan yang sangat baik dalam mendefinisikan baik monoid maupun monad, namun, mereka masih belum menjawab pertanyaan:

Dan pada catatan yang kurang penting, apakah ini benar dan jika demikian dapatkah Anda memberikan penjelasan (mudah-mudahan yang dapat dipahami oleh seseorang yang tidak memiliki banyak pengalaman Haskell)?

Inti dari masalah yang hilang di sini, adalah gagasan yang berbeda tentang "monoid", yang disebut kategorisasi lebih tepatnya - yang monoid dalam kategori monoid. Buku Sadly Mac Lane sendiri membuatnya sangat membingungkan :

Semua mengatakan, monad masuk Xhanya monoid dalam kategori endofunctors of X, dengan produk ×digantikan oleh komposisi endofunctors dan unit yang ditetapkan oleh endofunctor identitas.

Kebingungan utama

Mengapa ini membingungkan? Karena tidak mendefinisikan apa yang "monoid dalam kategori endofunctors" dari X. Sebaliknya, kalimat ini menyarankan mengambil monoid di dalam himpunan semua endofunctor bersama-sama dengan komposisi functor sebagai operasi biner dan functor identitas sebagai unit monoid. Yang berfungsi sangat baik dan berubah menjadi monoid bagian dari endofunctor yang berisi functor identitas dan ditutup di bawah komposisi functor.

Namun ini bukan interpretasi yang benar, yang gagal dijelaskan oleh buku ini pada tahap itu. Monad fadalah endofunctor tetap , bukan bagian dari endofunctor yang ditutup berdasarkan komposisi. Sebuah konstruksi umum adalah dengan menggunakan funtuk menghasilkan monoid dengan mengambil himpunan semua kkomposisi ganda f^k = f(f(...))dari fdengan dirinya sendiri, termasuk k=0yang sesuai dengan identitas f^0 = id. Dan sekarang himpunan Ssemua kekuatan ini untuk semua k>=0memang monoid "dengan produk × digantikan oleh komposisi endofunctor dan unit yang ditetapkan oleh endofunctor identitas".

Dan lagi:

  • Monoid ini Sdapat didefinisikan untuk setiap functor fatau bahkan secara harfiah untuk setiap peta diri X. Ini adalah monoid yang dihasilkan oleh f.
  • Struktur monoid yang Sdiberikan oleh komposisi functor dan functor identitas tidak ada hubungannya dengan fmenjadi atau tidak menjadi monad.

Dan untuk membuat hal-hal lebih membingungkan, definisi "monoid dalam kategori monoid" muncul nanti dalam buku seperti yang dapat Anda lihat dari daftar isi . Namun memahami gagasan ini sangat penting untuk memahami hubungan dengan monad.

(Ketat) kategori monoid

Pergi ke Bab VII tentang monoids (yang datang kemudian dari Bab VI tentang monad), kita menemukan definisi yang disebut kategori monoidal ketat sebagai tiga (B, *, e), di mana Badalah kategori, *: B x B-> Bsebuah bifunctor (functor terhadap masing-masing komponen dengan komponen lainnya tetap ) dan emerupakan objek satuan B, yang memenuhi asosiatif dan hukum unit:

(a * b) * c = a * (b * c)
a * e = e * a = a

untuk setiap benda a,b,cdari B, dan identitas yang sama untuk setiap morphisms a,b,cdengan edigantikan oleh id_e, para morphism identitas e. Sekarang menjadi pelajaran untuk mengamati bahwa dalam kasus yang kami minati, di mana Bkategori endofunctor Xdengan transformasi alami sebagai morfisme, *komposisi functor dan efunctor identitas, semua undang-undang ini dipenuhi, seperti yang dapat diverifikasi secara langsung.

Apa yang muncul setelahnya dalam buku ini adalah definisi dari kategori monoid "santai" , di mana hukum hanya menahan modulo beberapa transformasi alami tetap yang memuaskan apa yang disebut hubungan koherensi , yang bagaimanapun tidak penting bagi kasus kategori endofunctor kami.

Monoids dalam kategori monoid

Akhirnya, dalam bagian 3 "Monoids" dari Bab VII, definisi aktual diberikan:

Monoid cdalam kategori monoid (B, *, e)adalah objek Bdengan dua panah (morfisme)

mu: c * c -> c
nu: e -> c

membuat 3 diagram komutatif. Ingatlah bahwa dalam kasus kami, ini adalah morfisme dalam kategori endofunctor, yang merupakan transformasi alami yang sesuai dengan tepat joindan returnuntuk monad. Koneksi menjadi lebih jelas ketika kita membuat komposisi *lebih eksplisit, menggantikan c * cdengan c^2, di mana cmonad kita.

Akhirnya, perhatikan bahwa 3 diagram komutatif (dalam definisi monoid dalam kategori monoid) ditulis untuk kategori monoid umum (non-ketat), sedangkan dalam kasus kami semua transformasi alami yang timbul sebagai bagian dari kategori monoid sebenarnya adalah identitas. Itu akan membuat diagram persis sama dengan yang ada di definisi monad, membuat korespondensi selesai.

Kesimpulan

Singkatnya, setiap monad secara definisi adalah endofunctor, maka objek dalam kategori endofunctor, di mana monadik joindan returnoperator memenuhi definisi monoid dalam kategori monoidal (ketat) tertentu . Begitu pula sebaliknya, setiap monoid dalam kategori monoid endofunctor secara definisi adalah triple yang (c, mu, nu)terdiri dari sebuah objek dan dua panah, misalnya transformasi alami dalam kasus kami, memenuhi hukum yang sama dengan monad.

Akhirnya, perhatikan perbedaan utama antara monoids (klasik) dan monoids yang lebih umum dalam kategori monoid. Dua panah mudan di nuatas bukan lagi operasi biner dan unit dalam satu set. Sebagai gantinya, Anda memiliki satu endofunctor tetap c. Komposisi functor *dan functor identitas saja tidak menyediakan struktur lengkap yang diperlukan untuk monad, meskipun ada komentar membingungkan dalam buku ini.

Pendekatan lain akan membandingkan dengan monoid standar Cdari semua diri-peta set A, di mana operasi biner adalah komposisi, yang dapat dilihat untuk memetakan produk Cartesian standar C x Cdalam C. Melewati monoid yang dikategorikan, kami mengganti produk kartesius xdengan komposisi functor *, dan operasi biner diganti dengan transformasi alami mudari c * cmenjadi c, yaitu kumpulan joinoperator

join: c(c(T))->c(T)

untuk setiap objek T(ketik pemrograman). Dan elemen-elemen identitas dalam monoids klasik, yang dapat diidentifikasi dengan gambar-gambar peta dari satu-set-set tetap, diganti dengan koleksi returnoperator

return: T->c(T) 

Tetapi sekarang tidak ada lagi produk kartesius, sehingga tidak ada pasangan elemen dan dengan demikian tidak ada operasi biner.

Dmitri Zaitsev
sumber
Jadi apa jawaban Anda untuk bagian "apakah ini benar" dari pertanyaan? Benarkah monad adalah monoid dalam kategori endofunctor? Dan jika ya, apa hubungan antara gagasan teori kategori monoid dan monoid aljabar (satu set dengan perkalian asosiatif dan unit)?
Alexander Belopolsky