Was sind freie Monaden?

368

Ich habe den Begriff gesehen Freie Monad Pop - up jeden jetzt und dann für einige Zeit, aber jeder scheint nur zu benutzen / diskutieren sie , ohne eine Erklärung zu geben , was sie sind. Also: Was sind freie Monaden? (Ich würde sagen, ich bin mit Monaden und den Grundlagen von Haskell vertraut, habe aber nur sehr grobe Kenntnisse der Kategorietheorie.)

David
quelle
12
Eine ziemlich gute Erklärung finden Sie hier haskellforall.com/2012/06/…
Roger Lindsjö
19
@ Roger, das ist eine Art Seite, die mich hierher gebracht hat. Für mich definiert dieses Beispiel eine Monadeninstanz für einen Typ namens "Free" und das wars.
David

Antworten:

295

Die Antwort von Edward Kmett ist offensichtlich großartig. Aber es ist ein bisschen technisch. Hier ist eine vielleicht zugänglichere Erklärung.

Kostenlose Monaden sind nur eine allgemeine Methode, um Funktoren in Monaden zu verwandeln. Das heißt, vorausgesetzt, jeder Funktor f Free fist eine Monade. Dies wäre nicht sehr nützlich, es sei denn, Sie erhalten zwei Funktionen

liftFree :: Functor f => f a -> Free f a
foldFree :: Functor f => (f r -> r) -> Free f r -> r

Mit der ersten können Sie in Ihre Monade "hineinkommen", und mit der zweiten können Sie "herauskommen".

Im Allgemeinen ist ein "freies X" eine Möglichkeit, von einem Y zu einem X zu gelangen, ohne etwas zu gewinnen, wenn X ein Y mit einigen zusätzlichen Dingen P ist.

Beispiele: Ein Monoid (X) ist eine Menge (Y) mit einer zusätzlichen Struktur (P), die im Grunde sagt, dass es eine Operation (Sie können sich eine Addition vorstellen) und eine Identität (wie Null) hat.

Damit

class Monoid m where
   mempty  :: m
   mappend :: m -> m -> m

Jetzt kennen wir alle Listen

data [a] = [] | a : [a]

Nun, bei jedem Typ, den twir kennen, [t]ist das ein Monoid

instance Monoid [t] where
  mempty   = []
  mappend = (++)

und so sind Listen das "freie Monoid" über Mengen (oder in Haskell-Typen).

Okay, freie Monaden sind die gleiche Idee. Wir nehmen einen Funktor und geben eine Monade zurück. In der Tat, da Monaden als Monoide in der Kategorie der Endofunktoren angesehen werden können, die Definition einer Liste

data [a] = [] | a : [a]

sieht der Definition von freien Monaden sehr ähnlich

data Free f a = Pure a | Roll (f (Free f a))

und die MonadInstanz hat eine Ähnlichkeit mit der MonoidInstanz für Listen

--it needs to be a functor
instance Functor f => Functor (Free f) where
  fmap f (Pure a) = Pure (f a)
  fmap f (Roll x) = Roll (fmap (fmap f) x)

--this is the same thing as (++) basically
concatFree :: Functor f => Free f (Free f a) -> Free f a
concatFree (Pure x) = x
concatFree (Roll y) = Roll (fmap concatFree y)

instance Functor f => Monad (Free f) where
  return = Pure -- just like []
  x >>= f = concatFree (fmap f x)  --this is the standard concatMap definition of bind

Jetzt bekommen wir unsere beiden Operationen

-- this is essentially the same as \x -> [x]
liftFree :: Functor f => f a -> Free f a
liftFree x = Roll (fmap Pure x)

-- this is essentially the same as folding a list
foldFree :: Functor f => (f r -> r) -> Free f r -> r
foldFree _ (Pure a) = a
foldFree f (Roll x) = f (fmap (foldFree f) x)
Philip JF
quelle
12
Dies ist möglicherweise die beste ansprechbare Erklärung für "frei", die ich bisher gesehen habe. Besonders der Absatz, der mit "Allgemeiner" beginnt.
John L
16
Ich denke , es ist interessant zu sehen , Free f a = Pure a | Roll (f (Free f a))wie Free f a = a + fa + ffa + ..., dh „f zu einem beliebig oft angewandt“. Dann nimmt concatFree(dh join) ein "f, das beliebig oft auf (f wird beliebig oft auf a angewendet)" und reduziert die beiden verschachtelten Anwendungen zu einer. Und >>=nimmt "f beliebig oft auf a angewendet" und "wie man von a nach kommt (b mit f beliebig oft angewendet)" und wendet letzteres im Grunde auf a innerhalb des ersteren an und kollabiert die Verschachtelung. Jetzt verstehe ich es selbst!
JKFF
1
ist im concatFreeGrunde join?
Rgrinberg
11
„Hier ist eine vielleicht zugänglichere Erklärung. […] Da Monaden in der Kategorie der Endofunktoren als Monoide angesehen werden können,… “Dennoch halte ich dies für eine sehr gute Antwort.
Ruud
2
"Monaden können als Monoide in der Kategorie der Endofunktoren angesehen werden " <3 (Sie sollten auf stackoverflow.com/a/3870310/1306877 verlinken, da jeder Haskeller über diese Referenz Bescheid wissen sollte!)
bis
418

Hier ist eine noch einfachere Antwort: Eine Monade ist etwas, das "berechnet" wird, wenn der monadische Kontext durch reduziert wird join :: m (m a) -> m a(wobei daran erinnert wird, dass >>=dies definiert werden kann alsx >>= y = join (fmap y x) ). So führen Monaden den Kontext durch eine sequentielle Kette von Berechnungen: Denn an jedem Punkt der Reihe wird der Kontext des vorherigen Aufrufs mit dem nächsten reduziert.

Eine freie Monade erfüllt alle Monadengesetze, kollabiert jedoch nicht (dh berechnet). Es baut nur eine verschachtelte Reihe von Kontexten auf. Der Benutzer, der einen solchen freien monadischen Wert erstellt, ist dafür verantwortlich, etwas mit diesen verschachtelten Kontexten zu tun, sodass die Bedeutung einer solchen Komposition bis zur Erstellung des monadischen Werts verschoben werden kann.

John Wiegley
quelle
8
Ihre Absätze sind eine großartige Ergänzung zu Philipps Beitrag.
David
20
Diese Antwort gefällt mir sehr gut.
Danidiaz
5
Kann die freie Monade die Monadentypklasse ersetzen? Das heißt, ich könnte ein Programm schreiben, das nur die Rückgabe und Bindung der freien Monade verwendet, und dann die Ergebnisse mit dem von mir bevorzugten Mwybe oder List oder was auch immer verbinden oder sogar mehrere monadische Ansichten einer Sequenz von gebundenen / konkattierten Funktionsaufrufen generieren. Das heißt, Boden und Nichtterminierung ignorieren.
Misterbee
2
Diese Antwort hat geholfen, aber ich denke, es hätte mich verwirrt, wenn ich nicht auf dem NICTA-Kurs 'join' getroffen und haskellforall.com/2012/06/… gelesen hätte . Für mich besteht der Trick zum Verständnis darin, viele Antworten zu lesen, bis sie einsinken. (NICTA-Referenz: github.com/NICTA/course/blob/master/src/Course/Bind.hs )
Martin Capodici
1
Diese Antwort ist die beste aller Zeiten
Curycu
142

Ein freies Foo ist zufällig die einfachste Sache, die alle 'Foo'-Gesetze erfüllt. Das heißt, es erfüllt genau die Gesetze, die notwendig sind, um ein Foo zu sein, und nichts Besonderes.

Ein vergesslicher Funktor ist einer, der einen Teil der Struktur "vergisst", wenn er von einer Kategorie zur anderen wechselt.

Gegebene Funktoren F : D -> C, und G : C -> Dwir sagen F -| G, Fist links neben Goder Grechts neben, Fwann immer a, b: F a -> bisomorph ist a -> G b, wo die Pfeile aus den entsprechenden Kategorien stammen.

Formal bleibt ein freier Funktor neben einem vergesslichen Funktor.

Das freie Monoid

Beginnen wir mit einem einfacheren Beispiel, dem freien Monoid.

Nehmen Sie ein Monoid, das durch einen Trägersatz definiert ist T, eine binäre Funktion, um ein Elementpaar zusammenzufügen f :: T → T → T, und a unit :: T, so dass Sie ein assoziatives Gesetz und ein Identitätsgesetz haben : f(unit,x) = x = f(x,unit).

Sie können einen Funktor machen Uaus der Kategorie der Monoide (wo Pfeile Monoid Homomorphismen sind, das heißt, sie sicherstellen , dass sie Karte unitzu unitauf der anderen Monoid, und dass Sie vor oder nach der Abbildung auf die andere Monoid ohne Änderung Bedeutung komponieren kann) in die Kategorie von Sätzen (wobei Pfeile nur Funktionspfeile sind), die die Operation "vergessen" unitund Ihnen nur den Trägersatz geben.

Anschließend können Sie einen Funktor Faus der Kategorie der Sets zurück in die Kategorie der Monoide definieren, die neben diesem Funktor verbleibt. Dieser Funktor ist der Funktor, der eine Menge adem Monoid zuordnet [a], wo unit = []und mappend = (++).

Um unser bisheriges Beispiel in Pseudo-Haskell zu überprüfen:

U : Mon  Set -- is our forgetful functor
U (a,mappend,mempty) = a

F : Set  Mon -- is our free functor
F a = ([a],(++),[])

Dann, um zu zeigen, Fist kostenlos, müssen wir zeigen, dass es neben Ueinem vergesslichen Funktor bleibt, das heißt, wie wir oben erwähnt haben, müssen wir das zeigen

F a → b ist isomorph zu a → U b

Denken Sie nun daran, dass das Ziel von Fin der Kategorie Monder Monoide liegt, in der Pfeile Monoidhomomorphismen sind. Wir müssen also zeigen, dass ein Monoidhomomorphismus von [a] → bdurch eine Funktion von genau beschrieben werden kann a → b.

In Haskell nennen wir die Seite davon, in der wir leben Set(ähm, Haskdie Kategorie der Haskell-Typen, die wir vorgeben, ist Set), nur foldMapdie, wenn sie von Data.Foldableauf Listen spezialisiert ist, Typ hat Monoid m => (a → m) → [a] → m.

Daraus ergeben sich Konsequenzen als Ergänzung. Insbesondere, wenn Sie vergessen, dann bauen Sie mit kostenlos auf, dann vergessen Sie wieder, es ist genau so, wie Sie es einmal vergessen haben, und wir können dies verwenden, um den monadischen Join aufzubauen. da UFUF~ U(FUF)~ UFund wir den identitätsmonoiden Homomorphismus von [a]bis [a]durch den Isomorphismus übergeben können, der unsere Adjunktion definiert, erhalten Sie, dass ein Listenisomorphismus von [a] → [a]eine Funktion des Typs ista -> [a] , und dies ist nur eine Rückgabe für Listen.

Sie können dies alles direkter zusammenstellen, indem Sie eine Liste folgendermaßen beschreiben:

newtype List a = List (forall b. Monoid b => (a -> b) -> b)

Die freie Monade

Was ist also eine freie Monade? ?

Nun, wir machen das Gleiche wie zuvor. Wir beginnen mit einem vergesslichen Funktor U von der Kategorie der Monaden, bei der Pfeile Monadenhomomorphismen sind, zu einer Kategorie von Endofunktoren, bei denen die Pfeile natürliche Transformationen sind, und suchen nach einem Funktor, der nebeneinander bleibt dazu.

Wie hängt das mit der Vorstellung einer freien Monade zusammen, wie sie normalerweise verwendet wird?

Zu wissen, dass etwas eine freie Monade ist Free f, sagt Ihnen, dass das Geben eines Monadenhomomorphismus von Free f -> mdasselbe ist (isomorph zu) wie das Geben einer natürlichen Transformation (eines Funktorhomomorphismus) von f -> m. Denken Sie daran, F a -> bdass es isomorph zu sein muss, a -> U bdamit F neben U bleibt. Hier werden Monaden auf Funktoren abgebildet.

F ist mindestens isomorph zu dem FreeTyp, den ich in meinem freePaket für Hackage verwende.

Wir könnten es auch in engerer Analogie zum obigen Code für die freie Liste konstruieren, indem wir es definieren

class Algebra f x where
  phi :: f x -> x

newtype Free f a = Free (forall x. Algebra f x => (a -> x) -> x)

Kaffeefreie Comonaden

Wir können etwas Ähnliches konstruieren, indem wir den richtigen Zusatz zu einem vergesslichen Funktor betrachten, vorausgesetzt, er existiert. Ein Cofree-Funktor ist einfach / richtig neben einem vergesslichen Funktor, und aus Symmetriegründen ist das Wissen, dass etwas eine Cofree-Comonade ist, dasselbe wie das Wissen, dass das Geben eines Comonad-Homomorphismus aus w -> Cofree fdasselbe ist wie das Geben einer natürlichen Transformation von w -> f.

Edward KMETT
quelle
12
@PauloScardine, das ist nichts , was Sie haben zu besorgt sein. Meine Frage kam aus dem Interesse, eine fortgeschrittene Datenstruktur zu verstehen und vielleicht einen Einblick in die aktuellen Entwicklungen in der Haskell-Entwicklung zu bekommen - es ist keineswegs notwendig oder repräsentativ für das, worum es beim Schreiben von Haskell bisher eigentlich geht. (Und ein Heads-up, es wird besser, wenn Sie die IO-Lernphase wieder hinter sich haben)
David
8
@PauloScardine Sie benötigen die obige Antwort nicht, um in Haskell produktiv zu programmieren, selbst mit kostenlosen Monaden. Tatsächlich würde ich nicht empfehlen, die freie Monade auf diese Weise jemandem anzugreifen, der keinen Hintergrund in der Kategorietheorie hat. Es gibt viele Möglichkeiten, aus operativer Sicht darüber zu sprechen und zu lernen, wie man eine verwendet, ohne in die Kategorietheorie einzutauchen. Es ist mir jedoch unmöglich, die Frage zu beantworten, woher sie kommen, ohne in die Theorie einzutauchen. Freie Konstruktionen sind ein mächtiges Werkzeug in der Kategorietheorie, aber Sie benötigen diesen Hintergrund nicht, um sie zu verwenden.
Edward KMETT
18
@PauloScardine: Sie benötigen genau keinen Kalkül, um Haskell effektiv zu nutzen und sogar zu verstehen, was Sie tun. Es ist ein bisschen seltsam, sich darüber zu beschweren, dass "diese Sprache mathematisch ist", wenn die Mathematik nur zusätzliche Güte ist, die Sie für Spaß und Profit verwenden können. Sie erhalten diese Dinge nicht in den meisten zwingenden Sprachen. Warum würden Sie sich über Extras beschweren? Sie können sich einfach dafür entscheiden, NICHT mathematisch zu argumentieren und sich ihm wie jeder anderen neuen Sprache zu nähern.
Sarah
3
@ Sarah: Ich habe noch keine Dokumentation oder IRC-Konversation über Haskell gesehen, die nicht viel mit Computertheorie und Lambda-Kalkül-Thermos zu tun hat.
Paulo Scardine
11
@PauloScardine dies driftet ein wenig OT, aber zu Haskells Verteidigung: Ähnliche technische Dinge gelten für alle anderen Programmiersprachen, nur dass Haskell eine so schöne Zusammenstellung hat, dass die Leute tatsächlich gerne darüber sprechen können. Warum / wie X eine Monade ist, ist für viele Menschen interessant, Diskussionen über den IEEE-Gleitkomma-Standard jedoch nicht. Beide Fälle spielen für die meisten Menschen keine Rolle, da sie nur die Ergebnisse verwenden können.
David
72

Die freie Monade (Datenstruktur) ist für die Monade (Klasse) wie die Liste (Datenstruktur) für die Monoid (Klasse): Es ist die triviale Implementierung, bei der Sie anschließend entscheiden können, wie der Inhalt kombiniert werden soll.


Sie wissen wahrscheinlich, was eine Monade ist und dass jede Monade eine spezifische (monadengesetzliche) Implementierung von beiden benötigt fmap + join+ returnoder bind+ benötigt return.

Nehmen wir an, Sie haben einen Functor (eine Implementierung von fmap), aber der Rest hängt von den zur Laufzeit getroffenen Werten und Auswahlmöglichkeiten ab. Dies bedeutet, dass Sie die Monad-Eigenschaften verwenden möchten, aber anschließend die Monad-Funktionen auswählen möchten.

Dies kann mit der Free Monad (Datenstruktur) erfolgen, die den Functor (Typ) so umschließt, dass joines sich eher um eine Stapelung dieser Funktoren als um eine Reduzierung handelt.

Das Real returnund das, was joinSie verwenden möchten, können nun als Parameter für die Reduktionsfunktion angegeben werden foldFree:

foldFree :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
foldFree return join :: Monad m => Free m a -> m a

Um die Art zu erklären, können wir ersetzen Functor fmit Monad mund bmit (m a):

foldFree :: Monad m => (a -> (m a)) -> (m (m a) -> (m a)) -> Free m a -> (m a)
Comonad
quelle
8
Diese Antwort gab mir den Eindruck, dass ich verstehe, wofür sie überhaupt nützlich sein könnten.
David
59

Eine Haskell-freie Monade ist eine Liste von Funktoren. Vergleichen Sie:

data List a   = Nil    | Cons  a (List a  )

data Free f r = Pure r | Free (f (Free f r))

Pureist analog zu Nilund Freeist analog zu Cons. Eine kostenlose Monade speichert eine Liste von Funktoren anstelle einer Liste von Werten. Technisch gesehen könnten Sie freie Monaden mit einem anderen Datentyp implementieren, aber jede Implementierung sollte isomorph zu der obigen sein.

Sie verwenden kostenlose Monaden, wenn Sie einen abstrakten Syntaxbaum benötigen. Der Basisfunktor der freien Monade ist die Form jedes Schritts des Syntaxbaums.

Mein Beitrag , den bereits jemand verlinkt hat, enthält einige Beispiele für das Erstellen abstrakter Syntaxbäume mit freien Monaden

Gabriel Gonzalez
quelle
6
Ich weiß, dass Sie nur eine Analogie gezeichnet haben, anstatt eine Definition zu machen, aber eine freie Monade ist sicherlich in keiner Weise analog zu einer Liste von Funktoren. Es ist viel näher an einem Baum von Funktoren.
Tom Ellis
6
Ich stehe zu meiner Terminologie. Mit meinem Index-Core-Paket können Sie beispielsweise "freie Monadenverständnisse" definieren, die sich genau wie die Listenmonade verhalten, außer dass Sie Funktoren anstelle von Werten binden. Eine freie Monade ist eine Liste von Funktoren in dem Sinne, dass Listen zu freien Monaden werden, wenn Sie alle Haskell-Konzepte in die Kategorie der Funktoren übersetzen. Ein wahrer Funktorenbaum wird dann zu etwas ganz anderem.
Gabriel Gonzalez
4
Sie haben Recht, dass Monade in gewissem Sinne die Kategorisierung des Konzepts des Monoids ist, daher sind freie Monaden analog zu freien Monoiden, dh Listen. Insofern haben Sie sicherlich Recht. Die Struktur eines Wertes einer freien Monade ist jedoch keine Liste. Es ist ein Baum, wie ich weiter unten ausführlich erläutere .
Tom Ellis
2
@ TomEllis Technisch gesehen ist es nur ein Baum, wenn Ihr Basis-Funktor ein Produkt-Funktor ist. Wenn Sie einen Summenfunktor als Basisfunktor haben, ähnelt er eher einer Stapelmaschine.
Gabriel Gonzalez
21

Ich denke, ein einfaches konkretes Beispiel wird helfen. Angenommen, wir haben einen Funktor

data F a = One a | Two a a | Two' a a | Three Int a a a

mit dem Offensichtlichen fmap. Dann Free F aist die Art der Bäume , deren Blätter haben Art aund deren Knoten sind verschlagwortet mit One, Two, Two'und Three. One-Knoten haben ein Kind, Two- und Two'-Knoten haben zwei Kinder und Three-Knoten haben drei und sind auch mit einem gekennzeichnet Int.

Free Fist eine Monade. returnwird xdem Baum zugeordnet, der nur ein Blatt mit Wert ist x. t >>= fschaut auf jedes der Blätter und ersetzt sie durch Bäume. Wenn das Blatt einen Wert yhat, ersetzt es dieses Blatt durch den Baum f y.

Ein Diagramm macht dies klarer, aber ich habe nicht die Möglichkeit, einfach eines zu zeichnen!

Tom Ellis
quelle
14
Was ihr sagt, ist, dass die freie Monade die Form des Funktors selbst annimmt. Wenn der Funktor also baumartig ist (Produkte), ist die freie Monade baumartig; Wenn es listenartig ist (Summen), ist freie Monade listenartig; Wenn es funktionsähnlich ist, ist freie Monade funktionsähnlich. usw. Das macht für mich Sinn. Genau wie in einem freien Monoid behandeln Sie jede Anwendung von mappend immer wieder als ein völlig neues Element. In der kostenlosen Monade behandeln Sie jede Anwendung des Funktors als ein völlig neues Element.
Bartosz Milewski
4
Selbst wenn der Funktor ein "Summenfunktor" ist, ist die resultierende freie Monade immer noch baumartig. Sie haben mehr als einen Knotentyp in Ihrem Baum: einen für jede Komponente Ihrer Summe. Wenn Ihr "Summenfunktor" X -> 1 + X ist, erhalten Sie tatsächlich eine Liste, die nur eine entartete Baumart ist.
Tom Ellis