Primul care a spus următoarele?
O monadă este doar un monoid în categoria de endofunctors, ceea ce's problema?
Și pe o mai puțin important să rețineți, acest lucru este adevărat și dacă așa ar putea oferi o explicație (sperăm, una care poate fi înțeles de către cineva care nu't au mult Haskell experiență)?
Special frazare este de James Fumez, din foarte distractiv Scurtă, Incompletă și cea mai mare parte Greșită Istoria limbajelor de Programare, în care el ficțional atribute la Philip Wadler.
Citatul original este de la Saunders Mac Lane în Categorii pentru Lucru Matematician, unul din textele de bază din Teoria Categorie. Aici este în context, care este, probabil, cel mai bun loc pentru a ști exact ce înseamnă.
Dar, am'll ia o lovitură de cuțit. Sentința inițială este aceasta:
Toate-a spus, o monadă în X este doar un monoid în categoria de endofunctors de X, cu produs × înlocuit cu o compoziție de endofunctors și unitatea set de identitate endofunctor.
X aici este o categorie. Endofunctors sunt functori dintr-o categorie în sine (care este, de obicei, toate `Functor este pe cât de funcțional programatori sunt în cauză, deoarece acestea're cea mai mare parte de-a face doar cu o singură categorie; categoria de tipuri - dar am deviat de la subiect). Dar ai putea imagina o altă categorie care este categoria de "endofunctors pe X". Aceasta este o categorie în care obiectele sunt endofunctors și morfisme sunt naturale transformări.
Și de cei endofunctors, unele dintre ele ar putea fi monadele. Care sunt cele monadele? Exact cei care sunt monoidal într-un anumit sens. În loc de a scrie exact cartografiere de la monadele să monoids (din Mac Lane face asta mult mai bine decât aș putea spera să), I'll pune definiții lor alături și vă permite să comparați:
* -> * "cu o" Functor
exemplu)join
în Haskell)reveni
în Haskell)Cu un pic de cruciș s-ar putea fi capabil de a vedea că ambele definiții sunt instanțe de același abstract.
Intuitiv, cred că ceea ce fantezie matematica vocabular spune este că:
O monoid este un set de obiecte, și o metodă de a le combina. Bine cunoscut monoids sunt:
Acolo sunt mult mai complexe exemple de asemenea.
Mai mult, fiecare monoid are un identitatea**, care este ca "nu-op" element care nu are nici un efect atunci când se combina cu altceva:
În cele din urmă, un monoid trebuie să fie asociative. (puteți reduce un lung șir de combinații oricum doriți, atâta timp cât nu't schimba de la stânga la dreapta-comanda de obiecte) Plus este OK ((5+3)+1 == 5+(3+1)), dar scăderea nu este't ((5-3)-1 != 5-(3-1)).
Acum, las's ia în considerare un tip special de set și un mod special de a combina obiecte.
Să presupunem că setul contine obiecte de un tip special: funcții. Și aceste funcții au un interesant semnătura: Ei don't transporta numere numere sau siruri de caractere de la siruri de caractere. În schimb, fiecare funcție poartă un număr cu o listă de numere într-un proces în două etape.
Exemple:
De asemenea, modul nostru de a combina funcțiile speciale. Un mod simplu de a combina funcția este compozitie: Las's ia exemplele noastre de mai sus, și compune fiecare funcție cu sine:
Fără a intra prea mult în tipul de teorie, ideea este că puteți combina două numere întregi pentru a obține un număr întreg, dar poate't întotdeauna compune două funcții și a obține o funcție de același tip. (Funcții de tip a -> un va compune, dar a-> [a] a câștigat't.)
Deci, să's a defini un mod diferit de combinare a funcțiilor. Când ne-am combina aceste două funcții, nu ne't vreau să "dublu-wrap" rezultatele.
Aici este ceea ce facem. Atunci când vrem să combine două funcții F și G, urmăm acest proces (numit obligatoriu):
Înapoi la exemplele noastre, să's combina (bind) o funcție cu sine, folosind acest nou mod de "obligatoriu" funcții:
Acest mod mai sofisticat de a combina funcțiile de e asociative (urmare din funcția de compoziție este asociativă când nu't face fantezie ambalaj chestii).
Legându-l toate împreună,
Există o mulțime de moduri de a "folie de" rezultate. Puteți face o listă, sau un set, sau aruncați toate, dar primul rezultat, în timp ce observând dacă nu există nici un rezultat, atașați un ataș de stat, print-un mesaj de jurnal, etc, etc.
Am'am jucat un pic libere cu definițiile în speranța de a obține ideea esențială pe intuitiv.
Am'am simplificat un pic lucrurile, insistând că ne monadă funcționează pe funcții de tip a -> [o]. În fapt, monadele muncă pe funcții de tip a -> m b, dar generalizarea este un fel de detaliu tehnic pe care nu - 't principalele insight.
În primul rând, extensii și bibliotecile pe care le're de gând să utilizați:
{-# LANGUAGE RankNTypes, TypeOperators #-}
import Control.Monad (join)
Dintre acestea, RankNTypes
este singurul care's, absolut esențial pentru de mai jos. Am scris odată o explicație a RankNTypes
că unii oameni par să fi găsit util, așa că am'll se referă la faptul că.
Cu referire Tom Crockett's excelent raspuns, avem:
O monadă este...
- O endofunctor, T : X -> X
- Un transformării naturale, μ : T × T -> T, unde × mijloace functor compoziție
- Un transformării naturale, η : I -> T, unde I este identitatea endofunctor pe X
...satisface aceste legi:
- μ(μ(T × T) × T)) = μ(T × μ(T × T))
- μ(η(T)) = T = μ(T(η))
Cum putem traduce asta Haskell cod? Ei bine, las's începe cu noțiunea de naturale de transformare:
-- | 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 }
Un tip de forma f :-> geste analog cu o funcție de tip, dar în loc de gândire de ea ca un *funcția* între două *tipuri* (a fel
), cred că de ea ca un **morfism** între două **functori** (fiecare de un fel de
-> *`). Exemple:
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
Practic, în Haskell, transformari naturale sunt funcții de tip f x
pentru un alt tip g x
astfel încât " x " tip de variabilă este "inaccesibil" pentru a apelantului. Deci, de exemplu, sortare :: Ord a => [a] -> [o]
nu poate fi făcut într-un naturale de transformare, deoarece's "pretentios" despre ce tipuri putem instantia pentru "a". Un mod intuitiv eu folosesc de multe ori să cred că acest lucru este urmatoarea:
Acum, cu asta, las's a aborda clauze de definiție.
Prima clauză este "un endofunctor, T : X -> X." ei Bine, fiecare Functor în Haskell este un endofunctor în ceea ce oamenii numesc "Hask categorie," ale căror obiecte sunt Haskell tipuri (de genul
`) și a căror morfisme sunt Haskell funcții. Acest lucru sună ca un complicat declarație, dar's, de fapt, un foarte banal. Asta înseamnă că un Functor f :: -> oferă mijloacele de a construi o tip `f a :: pentru orice
o :: *și o funcție
fmap f :: f o -> f borice
f :: a -> b`, și că acestea se supun functor legi.
A doua clauză: "Identitatea" functor în Haskell (care vine cu Platforma, astfel încât să puteți importa doar ea) este definit astfel:
newtype Identity a = Identity { runIdentity :: a }
instance Functor Identity where
fmap f (Identity a) = Identity (f a)
Deci transformarea naturală η : I -> T de la Tom Crockett's definiție poate fi scris în acest fel pentru orice Monadă "exemplu" t`:
return' :: Monad t => Identity :-> t
return' = Natural (return . runIdentity)
A treia clauză: compoziția de doi functori în Haskell poate fi definit în acest fel (pe care, de asemenea, vine cu Platforma):
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)
Deci transformarea naturală μ : T × T -> T de la Tom Crockett's definiție poate fi scris astfel:
join' :: Monad t => Compose t t :-> t
join' = Natural (join . getCompose)
Afirmația că acesta este un monoid în categoria de endofunctors atunci înseamnă că Compune
(parțial aplicat doar primii doi parametri) este asociativă, și că "Identitate" este element de identitate. I. e., următoarele isomorphisms dețină:
Compune f (Compune g h) ~= Compunere (Compunere f g) h
Compune Identitate f ~= f
Compun Identitatea g ~= g
Acestea sunt foarte ușor pentru a dovedi că Compune
și "Identitate" sunt definite ca newtype
, și Haskell Rapoarte defini semantica newtype
ca un izomorfism între tipul fiind definit și tipul de argument newtype
's de date de constructor. Deci, de exemplu, să's a dovedi Compune Identitate f ~= 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.
Notă: nu, Nu e't de adevărat. La un moment dat a existat un comentariu la acest raspuns de la Dan Piponi el însuși spune că cauza și efect de aici a fost exact invers, că el a scris articolul, ca răspuns la James Fumez's quip. Dar se pare că a fost eliminat, probabil, de unele compulsiv mai ordonat.
Mai jos este al meu original răspuns.
L's destul de posibil ca Zână a citit De Monoids să Monadele, un post în care Dan Piponi (sigfpe) provine de monadele din monoids în Haskell, cu multe discuții din categoria teorie și mențiunea explicită a "categoria de endofunctors pe Hask" . În orice caz, nimeni care întreabă ce înseamnă pentru o monadă să fie un monoid în categoria de endofunctors ar putea beneficia de citit această derivare.
Am venit la acest post prin a înțelege mai bine inferență de faimosul citat de Mac Lane's Teoria Categorie Pentru Lucru Matematician. În descrierea ce este ceva, l's de multe ori la fel de utile pentru a descrie ceea ce se's nu. Faptul că Mac Lane folosește descriere pentru a descrie o Monadă, s-ar putea presupune că descrie ceva unic pentru monadele. Poarte cu mine. Pentru a dezvolta o mai bună înțelegere a situației, cred că trebuie să fie făcut clar faptul că el este nu descrie ceva care este unic pentru monadele; declarația la fel descrie Aplicative și Săgeți, printre altele. Pentru același motiv pentru care putem avea două monoids pe Int (Suma și Produsul), putem avea mai multe monoids pe X in categoria de endofunctors. Dar există chiar mai mult pentru a asemănărilor. Ambele Monadă și Aplicative îndeplinesc criteriile:
(de exemplu, în de zi cu zi `un Copac -> Lista b, dar în Categoria de Copac -> Lista)
Copac -> Lista
, doar Lista -> Lista. Declarația utilizează "Categorie de..." Aceasta definește domeniul de aplicare al declarației. Ca un exemplu, Functor Categorie descrie domeniul de aplicare de
f -> g , respectiv,
Orice functor -> Orice functor, de exemplu,
Tree -> Lista " sau " Tree -> Arbore . <P>Ce-o declarație Categorică nu specifica descrie unde _anything și totul este permitted_. <P>În acest caz, în interiorul functori,
-> aka
un -> bnu este specificat ceea ce înseamnă Nimic -> Orice, inclusiv Orice altceva
. Ca imaginatia mea sare la Int -> String, acesta include, de asemenea, Integer -> Poate Int "sau chiar" Poate Dublu -> Fie String Int
unde o :: Poate Dubla; b :: Fie Șirul Int
.
Atât declarația vine împreună, după cum urmează: :: f o -> g b
(de exemplu, orice parametrizate tip la orice parametrizate tip) :: f o -> f b
(adică, nici unul parametrizate tip la fel parametrizate tip) ... altfel spus, :: singur obiect -> singur obiect
), nu reușește să ilustrez faptul că'm-a permis să folosească o săgeată parametrizate cu any number de monoid valori, de la one tip obiect permisă în Monoid. La endo, ~ săgeată de identitate definiția echivalenței ignores de functor's type value și atât tipul și valoarea cea mai interioară, "sarcina utila" strat. Astfel, echivalența returnează "adevărat" în orice situație în care functorială tipuri se potrivesc (de exemplu, Nimic nu -> Doar -> Nimiceste echivalent cu
Doar -> Doar -> Doar deoarece acestea sunt atât de "Poate" - > Poate -> Poate
). Sidebar: ~ afara este conceptual, dar este cel mai lăsat simbol în `f-o`. Ea descrie, de asemenea, ceea ce "Haskell" citește-în primul rând (big picture); deci, este de Tip "afara" în ceea ce privește un Tip Valoare. Relația între straturi (un lanț de referințe) în programare nu este ușor să se refere în Categorie. Categoria de Set este folosit pentru a descrie Tipuri (Int, Siruri de caractere, Poate Int etc.) care include Categoria de Functor (Tipuri parametrizate). Referință lanț: Functor Tip, Functor valori (elemente de care Functor's a stabilit, de exemplu, Nimic, Doar), și, la rândul său, orice altceva fiecare functor valoare puncte. În Categoria relația este descrisă în mod diferit, de exemplu, `return :: a -> m` este considerat o persoană fizică transformarea dintr-un Functor la un alt Functor, diferit de orice menționate până acum. Înapoi la firul principal, toate în toate, pentru orice definită tensor produs și o valoare neutră, declarația sfârșește prin a descrie un uimitor de puternic calcul construct născut din paradoxal structura:
:: Lista
); static ori
asta nu spune nimic despre sarcină utilă) se alăture :: m (m, o) -> m-o
ca tensor produs pentru monoidal endofunctor. Cu toate acestea, nu articula cum, în contextul acestei declarații, (<*>)
ar putea, de asemenea, au fost de asemenea alese. Acesta este cu adevărat un exemplu de șase/jumătate de duzină. Logica pentru care combină valorile sunt exact la fel; același intrare generează aceeași ieșire din fiecare (spre deosebire de Suma și Produsul monoids pentru Int, deoarece acestea generează rezultate diferite atunci când combinarea Int).
Deci, să recapitulăm: Un monoid în categoria de endofunctors descrie: ~t :: m * -> m * -> m *
and a neutral value for m *
(<*>) " și "(>>=)
ambele oferă acces simultan la cei doi " m " valori în scopul de a calcula singur valoarea de returnare. Logica folosit pentru a calcula valoarea returnată este exact la fel. Dacă nu au fost pentru diferite forme de funcțiile pe care le parametrizarea (f :: a -> b "versus" k :: o -> m b
) și poziția parametru cu același tip de întoarcere de calcul (de exemplu, a -> b -> b "versus" b -> a -> b
pentru fiecare, respectiv), nu cred că ar putea fi parametrizate în monoidal logica, tensor produs, pentru reutilizare în ambele definiții. Ca un exercițiu pentru a se face înțeles, încercați să-și pună în aplicare ~t
, și va termina cu (<*>) " și " (>>=)
în funcție de modul în care decideți să-l definească pentrutoate un b
.
Dacă ultimul punct este la minim din punct de vedere conceptual adevărat, atunci explică precis, și numai în calcul diferența dintre Aplicativ și Monadă: funcțiile pe care le poate măsura. Cu alte cuvinte, diferența este external pentru punerea în aplicare a acestor clase de tip.
În concluzie, în propria mea experiență, Mac Lane's faimosul citat oferit un mare "du-te" meme, un guidepost pentru mine de referință în timp ce navigați prin felul meu de Categorie pentru a înțelege mai bine expresii utilizate în Haskell. Reușește la capturarea domeniul de aplicare de o puternică capacitate de calcul a făcut minunat accesibile în Haskell.
Cu toate acestea, există o ironie în modul în care am înțeles greșit declarația's aplicabilitatea în afara de monadă, și ceea ce sper transmis aici. Tot ceea ce descrie a dovedit a fi ceea ce este similar între Aplicative și Monade (și Săgeți printre altele). Ceea ce nu't spune este tocmai mic, dar util distincție între ele.
- E
Răspunsurile aici fac o treabă excelentă în definirea atât monoids și monade, cu toate acestea, ei încă nu't par să răspundă la întrebarea:
Și pe mai puțin important să rețineți, acest lucru este adevărat și dacă așa ar putea oferi o explicație (sperăm, una care poate fi înțeles de către cineva care nu't au mult Haskell experiență)? Esența problemei care lipsește aici, este diferit notiunea de "monoid", așa-numita categorification mai precis ... cel de monoid într-o categorie monoidal. Din păcate Mac Lane's carte de sine face foarte confuz: Toate-a spus, o monadă în " X "este doar un monoid în categoria de endofunctors de "X", cu produs
×
înlocuită cu compoziția de endofunctors și unitatea set de identitate endofunctor.Principala confuzie
De ce este această confuzie? Pentru că nu definește ceea ce este "monoid în categoria de endofunctors" de "X". În schimb, această frază sugerează lua un monoid în interiorul setul de toate endofunctors împreună cu functor compoziție ca operație binară și identitatea functor ca un monoidal unitate. Care funcționează foarte bine și se transformă într-un monoid orice subset de endofunctors care conține identitatea functor și este închis sub functor compoziție. Totuși, acest lucru nu este o interpretare corectă, pe care cartea nu reușește să facă clar în această etapă. O Monadă " f " este o fix endofunctor, nu un subset de endofunctors închis în compoziție. O construcție comună este de a folosi " f "a genera un monoid de a lua un set de toate" k "ori compoziții
f^k = f(f(...))
de" f " cu sine, inclusiv k=0care corespunde identitatea
f^0 = id. Și acum setat " S " de toate aceste puteri pentru toate
k>=0` este într-adevăr un monoid "cu produs × înlocuit cu o compoziție de endofunctors și unitatea set de identitate endofunctor". Și încă:
- Acest monoid " S "poate fi definit pentru orice functor" f "sau chiar la propriu pentru orice auto-harta de "X". Este monoid generate de "f".
- La monoidal structura " S "dat de functor compoziția și identitatea functor nu are nimic de-a face cu" f " a fi sau a nu fi o monadă. Și pentru a face lucrurile și mai confuze, definiția de "monoid în monoidal categoria" vine mai târziu în carte, după cum puteți vedea de la cuprins. Și totuși înțelege această noțiune este absolut esențială pentru înțelegerea legătură cu monadele.
(Strict) monoidal categorii
O să-Capitolul VII pe Monoids (care vine mai târziu decât Capitolul VI pe Monadele), găsim definiția de așa-numitele stricte categorie monoidal ca triple
(B, *, e), unde " B "este o categorie,
: B x B-> B` o bifunctor* (functor cu privire la fiecare componentă cu alte componente fixe) și" e "este o unitate de obiect în "B", satisfacerea asociativitatea și unitatea de legi:
(a * b) * c = a * (b * c)
a * e = e * a = a
pentru orice obiecte a,b,ca
B, și același identități pentru orice morfisme
a,b,c " cu " e "este înlocuită cu" id_e, identitatea morfism de "e". Acum este instructiv să observăm că în cazul nostru de interes, unde " B "este categoria de endofunctors de" X "cu transformari naturale ca morfisme,
` al functor compoziția și" e " identitatea functor, toate aceste legi sunt îndeplinite, ca pot fi verificate direct.
Ce vine după în carte este definiția de "egal" categorie monoidal, unde legile deține doar modulo fix unele transformari naturale îndeplinesc așa-numitele coerența relațiilor*, care însă nu este important pentru cazuri de endofunctor categorii.
În cele din urmă, în secțiunea 3 "Monoids" din Capitolul VII, reale definiție este dat de:
Un monoid " c "într-o categorie monoidal
(B, *, e)
este un obiect de "B", cu două săgeți (morfisme)
mu: c * c -> c
nu: e -> c
3 diagrame comutative. Reamintim că, în cazul nostru, acestea sunt morfisme in categoria de endofunctors, care sunt naturale transformări corespunde exact "participă" și "întors" pentru o monadă. Conexiunea devine și mai clară atunci când facem compoziție *
mai explicit, înlocuind `c * c " cu "c^2, unde" c " este noastre monadă.
În cele din urmă, observăm că 3 comutativ diagrame (în definiția de un monoid în monoidal categorie) sunt scrise pentru general (non-stricte) monoidal categorii, în timp ce în cazul nostru toate naturale transformări care apar ca parte a monoidal categorie sunt de fapt identitatea. Care va face diagrame exact la fel ca cei din definiția de o monadă, făcând corespondența completă.
În rezumat, orice monadă este, prin definiție, un endofunctor, prin urmare, un obiect din categoria de endofunctors, unde monadic "participă" și "întors" operatorii satisface definiția de un monoid în special (strict) categorie monoidal. Invers, orice monoid în categorie monoidal a endofunctors este, prin definiție, un triplu (c, mu, nu)
constând dintr-un obiect și două săgeți, de exemplu naturale transformări în cazul nostru, satisfăcând aceleași legi ca o monadă.
În cele din urmă, rețineți diferența esențială între (clasică) monoids și mai general monoids în monoidal categorii. Cele două săgeți mu
și nu
de mai sus nu mai este o operație binară și o unitate într-un set. În schimb, ai un fix endofunctor "c". La functor compoziție *
și identitatea functor singur nu oferă o structură necesare pentru monadă, în ciuda faptului că confuze remarcă în carte.
O altă abordare ar fi să comparăm cu standardul monoid " C "de toate auto-hărți de un set de "A", unde operație binară este structura, care poate fi văzut la harta standard de produs cartezian C x Cîn "C". Trecerea la categorified monoid, suntem înlocuirea produsului cartezian " x "cu functor compoziție"*", iar la operație binară este înlocuit cu transformarea naturală
mudin
c * ca
c`, care este o colecție de "adere" operatorii
join: c(c(T))->c(T)
pentru fiecare obiect " T " (tastați în programare). Și elemente de identitate clasică monoids, care pot fi identificate cu imagini de hărțile de pe un fix într-un punct-set, înlocuit cu o colecție de "returnare" operatorii
return: T->c(T)
Dar acum nu mai sunt produse carteziene, deci nu perechi de elemente și astfel nu operații binare.