Ich bin zu diesem Beitrag gekommen, weil ich die Schlussfolgerung des berüchtigten Zitats aus Mac Lane's Kategorientheorie für den arbeitenden Mathematiker .
Wenn man beschreibt, was etwas ist, ist es oft ebenso nützlich zu beschreiben, was es nicht ist.
Die Tatsache, dass Mac Lane die Beschreibung verwendet, um eine Monade zu beschreiben, könnte darauf hindeuten, dass sie etwas beschreibt, das nur für Monaden gilt. Seien Sie nachsichtig mit mir. Um ein breiteres Verständnis der Aussage zu entwickeln, muss meines Erachtens deutlich gemacht werden, dass er no beschreibt etwas, das es nur bei Monaden gibt; die Aussage gilt auch für Applicative und Arrows und andere. Aus demselben Grund, aus dem wir zwei Monoide auf Int (Summe und Produkt) haben können, können wir mehrere Monoide auf X in der Kategorie der Endofunktionen haben. Aber die Ähnlichkeiten gehen noch weiter.
Sowohl Monad als auch Applicative erfüllen die Kriterien:
-
endo => jeder Pfeil oder Morphismus, der an der gleichen Stelle beginnt und endet
-
Funktor => beliebiger Pfeil oder Morphismus zwischen zwei Kategorien
(z. B. im täglichen Leben Tree a -> List b
sondern in der Kategorie Tree -> List
)
-
monoid => einzelnes Objekt; d.h. ein einziger Typ, aber in diesem Zusammenhang nur in Bezug auf die externe Schicht; wir können also nicht Tree -> List
nur List -> List
.
Die Erklärung verwendet "Kategorie von...". Damit wird der Geltungsbereich der Aussage definiert. Als Beispiel beschreibt die Functor Category den Geltungsbereich von f * -> g *
d.h., Any functor -> Any functor
, z.B., Tree * -> List *
o Tree * -> Tree *
.
Was eine kategorische Aussage nicht angibt, beschreibt, wo alles und jedes ist erlaubt .
In diesem Fall innerhalb der Funktoren, * -> *
alias a -> b
nicht angegeben ist, was bedeutet Anything -> Anything including Anything else
. Da meine Vorstellungskraft auf Int -> String überspringt, beinhaltet sie auch Integer -> Maybe Int
oder sogar Maybe Double -> Either String Int
donde a :: Maybe Double; b :: Either String Int
.
Die Erklärung lässt sich also wie folgt zusammenfassen:
- Funktorbereich
:: f a -> g b
(d.h. jeder parametrisierte Typ zu jedem parametrisierten Typ)
- endo + functor
:: f a -> f b
(d.h. ein beliebiger parametrisierter Typ auf denselben parametrisierten Typ) ... anders gesagt,
- ein Monoid in der Kategorie der Endofunktoren
Wo liegt also die Kraft dieses Konstrukts? Um die volle Dynamik zu verstehen, musste ich sehen, dass die typischen Zeichnungen eines Monoids (ein einzelnes Objekt mit einem Pfeil, der wie eine Identität aussieht) nicht ausreichen, :: single object -> single object
), zeigt nicht, dass ich einen Pfeil verwenden darf, der mit beliebige Nummer von monoiden Werten, aus dem eine Typ Objekt in Monoid erlaubt. Die endo, ~ Identitätspfeil-Definition der Äquivalenz ignoriert des Funktors Typwert und sowohl den Typ als auch den Wert der innersten Schicht, der "Nutzlast". Somit ergibt die Äquivalenz true
in jeder Situation, in der die Funktionstypen übereinstimmen (z. B., Nothing -> Just * -> Nothing
ist gleichbedeutend mit Just * -> Just * -> Just *
denn sie sind beide Maybe -> Maybe -> Maybe
).
Sidebar: ~ outside ist begrifflich, aber es ist das Symbol ganz links in f a
. Es beschreibt auch, was "Haskell" zuerst einliest (großes Bild); so ist Type "draußen" in Bezug auf einen Type Value. Die Beziehung zwischen Ebenen (eine Kette von Referenzen) in der Programmierung ist in der Kategorie nicht einfach zu beschreiben. Die Kategorie der Menge wird verwendet, um Typen (Int, Strings, Maybe Int usw.) zu beschreiben, die die Kategorie des Funktors (parametrisierte Typen) einschließt. Die Referenzkette: Funktortyp, Funktorwerte (Elemente der Menge dieses Funktors, z. B. Nothing, Just) und wiederum alles andere, worauf jeder Funktorwert verweist. In Category wird die Beziehung anders beschrieben, z.B., return :: a -> m a
wird als eine natürliche Transformation von einem Funktor in einen anderen Funktor betrachtet, die sich von allen bisher genannten unterscheidet.
Zurück zum Hauptthema: Alles in allem beschreibt die Aussage für jedes definierte Tensorprodukt und einen neutralen Wert ein erstaunlich leistungsfähiges Berechnungskonstrukt, das aus seiner paradoxen Struktur entsteht:
- nach außen hin erscheint es als ein einziges Objekt (z.B.,
:: List
); statisch
- aber im Inneren eine Menge Dynamik zulässt
- eine beliebige Anzahl von Werten desselben Typs (z. B. Empty | ~NonEmpty) als Futter für Funktionen beliebiger Arität. Das Tensorprodukt reduziert eine beliebige Anzahl von Eingaben auf einen einzigen Wert... für die externe Schicht (~
fold
die nichts über die Nutzlast aussagt)
- unendliche Reihe von beide den Typ und die Werte für die innerste Schicht
In Haskell ist die Klärung der Anwendbarkeit der Anweisung wichtig. Die Macht und Vielseitigkeit dieses Konstrukts hat absolut nichts mit einer Monade an sich zu tun. Mit anderen Worten, das Konstrukt beruht nicht auf dem, was eine Monade einzigartig macht.
Wenn man versucht, herauszufinden, ob man Code mit einem gemeinsamen Kontext erstellen soll, um Berechnungen zu unterstützen, die voneinander abhängen, oder Berechnungen, die parallel ausgeführt werden können, ist diese berüchtigte Aussage, so viel sie auch beschreibt, kein Gegensatz zwischen der Wahl von Applicative, Arrows und Monads, sondern eher eine Beschreibung, wie sehr sie gleich sind. Für die vorliegende Entscheidung ist die Aussage überflüssig.
Dies wird oft missverstanden. In der Erklärung wird weiter beschrieben join :: m (m a) -> m a
als Tensorprodukt für den monoidalen Endofunktor. Es wird jedoch nicht dargelegt, wie im Zusammenhang mit dieser Aussage, (<*>)
hätte auch gewählt werden können. Es ist wirklich ein Beispiel für 'sechs in einem, ein halbes Dutzend im anderen'. Die Logik für die Kombination von Werten ist genau gleich; dieselbe Eingabe erzeugt dieselbe Ausgabe (im Gegensatz zu den Summen- und Produktmonoiden für Int, da sie bei der Kombination von Ints unterschiedliche Ergebnisse erzeugen).
Also, um es noch einmal zusammenzufassen: Ein Monoid in der Kategorie der Endofunktoren beschreibt:
~t :: m * -> m * -> m *
and a neutral value for m *
(<*>)
et (>>=)
beide ermöglichen den gleichzeitigen Zugang zu den beiden m
Werte, um den einzelnen Rückgabewert zu berechnen. Die Logik, mit der der Rückgabewert berechnet wird, ist genau dieselbe. Wären da nicht die unterschiedlichen Formen der Funktionen, die sie parametrisieren ( f :: a -> b
gegen k :: a -> m b
) und die Position des Parameters mit demselben Rückgabetyp der Berechnung (d. h., a -> b -> b
gegen b -> a -> b
für beide), so vermute ich, dass wir die monoidale Logik, das Tensorprodukt, zur Wiederverwendung in beiden Definitionen hätten parametrisieren können. Versuchen Sie zur Veranschaulichung, Folgendes zu implementieren ~t
und am Ende erhält man (<*>)
et (>>=)
je nachdem, wie Sie es definieren möchten forall a b
.
Wenn mein letzter Punkt zumindest konzeptionell zutreffend ist, dann erklärt er den genauen und einzigen rechnerischen Unterschied zwischen Applicative und Monad: die Funktionen, die sie parametrisieren. Mit anderen Worten, der Unterschied ist extern zur Implementierung dieser Typklassen.
Abschließend kann ich sagen, dass Mac Lanes berüchtigtes Zitat für mich ein großartiges "goto"-Memo war, ein Wegweiser, auf den ich mich beziehen konnte, während ich mich durch die Kategorie navigierte, um die in Haskell verwendeten Idiome besser zu verstehen. Es gelingt, den Umfang einer mächtigen Rechenkapazität zu erfassen, die in Haskell auf wunderbare Weise zugänglich gemacht wird.
Es liegt jedoch eine gewisse Ironie darin, dass ich die Anwendbarkeit der Aussage außerhalb der Monade zunächst missverstanden habe, was ich hier hoffentlich vermittelt habe. Alles, was sie beschreibt, stellt sich als das heraus, was zwischen Applicative und Monads (und Arrows unter anderem) ähnlich ist. Was sie nicht sagt, ist genau der kleine, aber nützliche Unterschied zwischen ihnen.
25 Stimmen
Siehe "Kategorien für den Arbeitsmathematiker".
37 Stimmen
Sie müssen das nicht verstehen, um Monaden in Haskell zu verwenden. Aus praktischer Sicht sind sie nur ein cleverer Weg, um "Zustände" durch unterirdische Leitungen weiterzuleiten.
3 Stimmen
Ich möchte auch diesen ausgezeichneten Blogbeitrag hier hinzufügen: stephendiehl.com/posts/monads.html Es beantwortet zwar nicht direkt die Frage, aber meiner Meinung nach leistet Stephen eine hervorragende Arbeit, um Kategorien und Monaden in Haskell miteinander zu verbinden. Wenn Sie die obigen Antworten gelesen haben, sollte dies helfen, die beiden Sichtweisen zu vereinen.
5 Stimmen
Genauer gesagt: "Für jede beliebige Kategorie C hat die Kategorie [C,C] ihrer Endofunktoren eine monoidale Struktur, die durch die Komposition induziert wird. Ein monoides Objekt in [C,C] ist eine Monade auf C." - aus en.wikipedia.org/wiki/Monoid_%28category_theory%29. Siehe en.wikipedia.org/wiki/Monade_%28Kategorientheorie%29 für die Definition der Monade in der Kategorientheorie.
1 Stimmen
Ich habe jetzt über ein Jahr damit verbracht, über Haskell nachzudenken und ich kann immer noch nicht sicher verstehen, was ein Funktor ist (ist es ein Funktionsobjekt? ein Objekt, über das man abbilden kann? eine Funktion, die a nimmt und M a zurückgibt? Eine binäre Funktion, die a annimmt und M a zurückgibt? Wie kann man eine Funktion abbilden, wenn sie keine Elemente hat, über die man iterieren kann...) Ganz zu schweigen davon, was ein Endofunctor ist. Ich verstehe, dass man mit fmap eine Funktion auf ein Boxed-Objekt anwenden kann, und mit >>= kann man ein Boxed-Objekt von M in eine Funktion a -> M a schieben, aber was nun?
3 Stimmen
@Dmitry A Funktor ist eine Funktion zwischen den Kategorien, die einigen Einschränkungen unterliegt, um gut zu sein. Ein Endofaktor auf einer Kategorie C ist einfach ein Funktor von C zu sich selbst. Data.Functor ist eine Typklasse für Endofunktoren auf der Kategorie Hask . Da eine Kategorie aus Objekten und Morphismen besteht, muss ein Funktor beides abbilden. Für eine Instanz f von Data.Functor ist die Abbildung auf Objekte (Haskell-Typen) f selbst und die Abbildung auf Morphismen (Haskell-Funktionen) ist fmap.
2 Stimmen
Hier finden Sie eine präzise, aber menschliche Erklärung: stackoverflow.com/questions/2704652/
2 Stimmen
Siehe die brillante Vortragsreihe von Bartosz Milewski Kategorientheorie für Programmierer für die ganze Geschichte. Darin legt Bartosz die erforderlichen Voraussetzungen in der Kategorientheorie fest, wobei er immer wieder auf Haskell zurückgreift. Der letzte Teil heißt eigentlich Monoid in der Kategorie der Endofunktoren und beantwortet die Frage vollständig.
0 Stimmen
Man hat das Gefühl, dass die Mathematiker, die diesen Begriff erfunden haben, ursprünglich nie über die Konzepte dahinter nachgedacht haben. Sie dachten über ein bestimmtes Problem nach und gaben ihm einen Namen. Definitionen wie diese spiegeln selten die dahinter stehenden Ideen wider. Ich glaube, dass einer guten Definition immer ein Kontext und verbundene Begriffe vorausgehen müssen.