Widziałem termin Monada bezpłatny pojawiają się za każdym teraz i potem przez jakiś czas, ale wszyscy po prostu wydaje się używać / omówić je bez podania wyjaśnienia, jakie są. Czym są darmowe monady? (Powiedziałbym, że jestem zaznajomiony z monadami i podstawami Haskella, ale mam tylko bardzo przybliżoną wiedzę na temat teorii kategorii.)
haskell
monads
free-monad
David
źródło
źródło
Odpowiedzi:
Odpowiedź Edwarda Kmetta jest oczywiście świetna. Ale to jest trochę techniczne. Oto być może bardziej przystępne wyjaśnienie.
Darmowe monady to tylko ogólny sposób na przekształcenie funktorów w monady. To znaczy, biorąc pod uwagę, że każdy funktor
f
Free f
jest monadą. Nie byłoby to bardzo przydatne, z wyjątkiem pary funkcjipierwszy z nich pozwala „dostać się” do monady, a drugi umożliwia „wydostanie się” z niej.
Mówiąc bardziej ogólnie, jeśli X to Y z dodatkowymi dodatkami P, to „wolny X” jest sposobem na przejście od Y do X bez uzyskiwania czegoś dodatkowego.
Przykłady: monoid (X) to zbiór (Y) z dodatkową strukturą (P), który w zasadzie mówi, że ma operację (możesz pomyśleć o dodaniu) i pewną tożsamość (jak zero).
Więc
Teraz wszyscy znamy listy
Cóż, biorąc pod uwagę każdy typ,
t
który wiemy, że[t]
jest monoidemdlatego listy są „wolnymi monoidami” nad zestawami (lub w typach Haskella).
Okej, więc wolne monady to ten sam pomysł. Bierzemy funktor i oddajemy monadę. W rzeczywistości, ponieważ monady można postrzegać jako monoidy w kategorii endofunkcji, definicja listy
wygląda bardzo podobnie do definicji wolnych monad
i
Monad
instancja ma podobieństwo doMonoid
instancji dla listteraz mamy dwie operacje
źródło
Free f a = Pure a | Roll (f (Free f a))
jakoFree f a = a + fa + ffa + ...
„f zastosowane dowolną liczbę razy”. NastępnieconcatFree
(tj.join
) Bierze „f zastosowane dowolną liczbę razy do (f zastosowane dowolną liczbę razy do a)” i zwija dwie zagnieżdżone aplikacje w jedną. I>>=
bierze „f zastosowane dowolną liczbę razy do a” i „jak dostać się od a do (b przy f zastosowane dowolną liczbę razy)”, i zasadniczo stosuje to drugie do a wewnątrz tego pierwszego i zwija gniazdo. Teraz sam to rozumiem!concatFree
zasadziejoin
?Oto jeszcze prostsza odpowiedź: monada jest czymś, co „oblicza się”, gdy kontekst monadyczny jest zwinięty
join :: m (m a) -> m a
(przypominając, że>>=
można to zdefiniować jakox >>= y = join (fmap y x)
). W ten sposób Monady przenoszą kontekst przez sekwencyjny łańcuch obliczeń: ponieważ w każdym punkcie serii kontekst z poprzedniego wywołania jest zawijany do następnego.A darmowe Monad spełnia wszystkie ustawowe Monad, ale nie robi żadnego zawaleniem (tj obliczeń). Po prostu tworzy zagnieżdżoną serię kontekstów. Użytkownik, który tworzy taką wolną wartość monadyczną, jest odpowiedzialny za zrobienie czegoś z tymi zagnieżdżonymi kontekstami, tak aby znaczenie takiej kompozycji można było odłożyć do momentu utworzenia wartości monadycznej.
źródło
Bezpłatne foo okazuje się najprostszą rzeczą, która spełnia wszystkie prawa „foo”. To znaczy, że spełnia dokładnie prawa niezbędne do bycia foo i nic więcej.
Zapominający funktor to taki, który „zapomina” część struktury, przechodząc z jednej kategorii do drugiej.
Biorąc pod uwagę funktory
F : D -> C
, iG : C -> D
, jak mówimyF -| G
,F
jest on przyległy doG
, lubG
jest przyległy do,F
ilekroć forall a, b:F a -> b
jest izomorficznya -> G b
, gdzie strzałki pochodzą z odpowiednich kategorii.Formalnie wolny funktor pozostaje przyległy do zapomnianego funktora.
Wolny Monoid
Zacznijmy od prostszego przykładu, wolnej monoidy.
Weź monoid, który jest zdefiniowany przez jakiś zestaw nośnej
T
, funkcja binarna mash parę elementów ze sobąf :: T → T → T
, a takżeunit :: T
takie, które mają prawo zrzeszania się, prawo oraz tożsamości:f(unit,x) = x = f(x,unit)
.Możesz zrobić funktor
U
z kategorii monoidów (gdzie strzałki są homomorfizmami monoidów, tzn. Zapewniają, że odwzorowująunit
sięunit
na innym monoidie, i że możesz komponować przed lub po mapowaniu na drugą monoidę bez zmiany znaczenia) do kategorii zestawów (gdzie strzałki są tylko strzałkami funkcyjnymi), które „zapominają” o operacjiunit
i po prostu dają ci zestaw nośny.Następnie możesz zdefiniować funktor
F
z kategorii zestawów z powrotem do kategorii monoidów, która pozostaje przyległa do tego funktora. Ten funktor jest funktorem, który odwzorowuje zbióra
na monoid[a]
, gdzieunit = []
imappend = (++)
.Aby przejrzeć nasz dotychczasowy przykład w pseudo-Haskell:
Następnie pokazanie
F
jest bezpłatne, musimy wykazać, że jest on sąsiadujący zU
zapomnianym funktorem, to znaczy, jak wspomniano powyżej, musimy pokazać, żeF a → b
jest izomorficznya → U b
pamiętajcie teraz, że cel
F
jest w kategoriiMon
monoidów, gdzie strzały są homomorfizmami monoidów, więc musimy pokazać, że homomorfizm monoidów z[a] → b
można dokładnie opisać funkcjąa → b
.W Haskell nazywamy stronę tego, w której żyjemy
Set
(er,Hask
kategoria typów Haskell, którą udajemy, jest ustawiona), właśniefoldMap
, która, gdy specjalizujemyData.Foldable
się w Listach, ma typMonoid m => (a → m) → [a] → m
.Istnieją konsekwencje wynikające z tego, że jest to dodatek. Zwłaszcza, że jeśli zapomnisz, a następnie zbuduj za darmo, to zapomnij jeszcze raz, tak jak raz zapomniałeś, a my możemy to wykorzystać do zbudowania połączenia monadycznego. ponieważ
UFUF
~U(FUF)
~UF
, i możemy przekazać tożsamość monomorfizmu monoidu od[a]
do[a]
poprzez izomorfizm, który określa nasze przyleganie, uzyskajmy, że izomorfizm listy od[a] → [a]
jest funkcją typua -> [a]
, a to jest po prostu powrót do list.Możesz to wszystko skomponować bardziej bezpośrednio, opisując listę w tych kategoriach za pomocą:
Wolna monada
Czym jest wolna monada ?
Cóż, robimy to samo, co poprzednio, zaczynamy od zapomnianego funktora U z kategorii monad, w których strzały są homomorfizmami monad, do kategorii endofunkcji, w których strzały są naturalnymi transformacjami, i szukamy funktora, który jest pozostawiony obok siebie do tego.
Jak to się ma do pojęcia wolnej monady, jak się zwykle używa?
Wiedząc, że coś jest wolną monadą,
Free f
mówi ci, że nadanie monomorfizmowi monady zFree f -> m
, jest tym samym (izomorficznym), co nadanie naturalnej transformacji (homomorfizm funktorski)f -> m
. Pamiętaj, żeF a -> b
musi być izomorficzny, abya -> U b
F pozostawał przyległy do U. U tutaj mapowane monady na funktory.F jest co najmniej izomorficzny do
Free
typu używanego w moimfree
pakiecie podczas włamań.Możemy również skonstruować go w ścisłej analogii do powyższego kodu dla wolnej listy, definiując
Cofree Comonady
Możemy skonstruować coś podobnego, patrząc na właściwe połączenie z zapomnianym funktorem, zakładając, że istnieje. Funktor Cofree jest po prostu / właściwie przylegający / do zapomnianego funktora, a symetria, wiedząc, że coś jest cofree comonad, jest tym samym, co wiedza, że nadanie homomorfizmu comonad z
w -> Cofree f
jest tym samym, co danie naturalnej transformacjiw -> f
.źródło
Free Monad (struktura danych) odnosi się do Monady (klasy), podobnie jak List (struktura danych) do Monoid (klasy): Jest to trywialna implementacja, w której możesz później zdecydować, w jaki sposób zawartość zostanie połączona.
Prawdopodobnie wiesz, czym jest Monada i że każda Monada potrzebuje specyficznej (zgodnej z prawem Monady) implementacji
fmap
+join
+return
lubbind
+return
.Załóżmy, że masz Functor (implementacja
fmap
), ale reszta zależy od wartości i wyborów dokonanych w czasie wykonywania, co oznacza, że chcesz móc korzystać z właściwości Monady, ale później wybrać funkcje Monady.Można tego dokonać za pomocą Free Monad (struktura danych), która otacza Functor (typ) w taki sposób, że
join
jest to raczej układanie tych funktorów niż redukcja.Rzeczywiste
return
ijoin
chcesz użyć, można teraz podać jako parametry funkcji redukcjifoldFree
:Aby wyjaśnić rodzajów, możemy wymienić
Functor f
zMonad m
orazb
z(m a)
:źródło
Monada wolna od Haskella to lista funktorów. Porównać:
Pure
jest analogiczny doNil
iFree
jest analogiczny doCons
. Wolna monada przechowuje listę funktorów zamiast listy wartości. Technicznie rzecz biorąc, możesz implementować wolne monady przy użyciu innego typu danych, ale każda implementacja powinna być izomorficzna do powyższej.Korzystasz z bezpłatnych monad, gdy potrzebujesz abstrakcyjnego drzewa składni. Podstawowym funktorem wolnej monady jest kształt każdego kroku drzewa składniowego.
Mój post , który ktoś już powiązał, podaje kilka przykładów, jak budować abstrakcyjne drzewa składniowe za pomocą wolnych monad
źródło
Myślę, że prosty konkretny przykład pomoże. Załóżmy, że mamy funktor
z oczywistym
fmap
. WtedyFree F a
jest rodzaj drzew, których liście mają rodzaja
i której węzły są otagowaneOne
,Two
,Two'
iThree
.One
-nodes mają jedno dziecko,Two
- iTwo'
-nodes mają dwa dzieci, aThree
-nodes ma trzy i są również oznaczone tagiemInt
.Free F
jest monadą.return
mapujex
na drzewo, które jest tylko liściem o wartościx
.t >>= f
patrzy na każdy z liści i zastępuje je drzewami. Gdy liść ma wartośćy
, zastępuje ten liść drzewemf y
.Schemat czyni to jaśniejszym, ale nie mam możliwości łatwego narysowania jednego!
źródło