Ich bin zu diesem Beitrag gekommen, um die Schlussfolgerung des berüchtigten Zitats aus Mac Lanes Kategorietheorie für den Arbeitsmathematiker besser zu verstehen .
Bei der Beschreibung, was etwas ist, ist es oft gleichermaßen nützlich, zu beschreiben, was es nicht ist.
Die Tatsache, dass Mac Lane die Beschreibung verwendet, um eine Monade zu beschreiben, könnte bedeuten, dass sie etwas Einzigartiges für Monaden beschreibt. Trage es mit mir. Um ein breiteres Verständnis der Aussage zu entwickeln, muss meines Erachtens klargestellt werden, dass er nicht etwas beschreibt, das nur für Monaden gilt. Die Aussage beschreibt unter anderem Applicative und Arrows gleichermaßen. Aus dem gleichen Grund können wir zwei Monoide auf Int (Summe und Produkt) haben, wir können mehrere Monoide auf X in der Kategorie der Endofunktoren haben. Aber die Ähnlichkeiten haben noch mehr zu bieten.
Sowohl Monad als auch Applicative erfüllen die Kriterien:
Die Anweisung verwendet "Kategorie von ...". Dies definiert den Umfang der Anweisung. Als Beispiel beschreibt die Funktorkategorie den Umfang f * -> g *
, das heißt Any functor -> Any functor
, zum Beispiel Tree * -> List *
oder Tree * -> Tree *
.
Was eine kategoriale Aussage nicht spezifiziert, beschreibt, wo alles und jedes erlaubt ist .
In diesem Fall ist innerhalb der Funktoren * -> *
aka a -> b
nicht angegeben, was bedeutet Anything -> Anything including Anything else
. Wenn meine Fantasie zu Int -> String springt, schließt es auch ein Integer -> Maybe Int
oder sogar Maybe Double -> Either String Int
wo a :: Maybe Double; b :: Either String Int
.
Die Aussage lautet also wie folgt:
- Funktionsbereich
:: f a -> g b
(dh ein beliebiger parametrisierter Typ zu einem beliebigen parametrisierten Typ)
- endo + functor
:: f a -> f b
(dh jeder parametrisierte Typ zum gleichen parametrisierten Typ) ... anders gesagt,
- ein Monoid in der Kategorie Endofunctor
Wo liegt also die Kraft dieses Konstrukts? Um die volle Dynamik zu erkennen, musste ich sehen, dass die typischen Zeichnungen eines Monoids (einzelnes Objekt mit einem Identitätspfeil :: single object -> single object
) nicht veranschaulichen, dass ich einen Pfeil verwenden darf, der mit einer beliebigen Anzahl von Monoidwerten parametrisiert ist. von dem einem Typ Objekt erlaubte in Monoid. Die endo, ~ Identität Pfeil Definition der Gleichwertigkeit ignoriert die von Funktors Typwert und sowohl die Art und den Wert der innersten „Nutzlast“ Schicht. Somit kehrt die Äquivalenz true
in jeder Situation zurück, in der die Funktionstypen übereinstimmen (z. B. Nothing -> Just * -> Nothing
ist äquivalent zu, Just * -> Just * -> Just *
weil sie beide sind Maybe -> Maybe -> Maybe
).
Seitenleiste: ~ außerhalb ist konzeptionell, aber das Symbol ganz links in f a
. Es beschreibt auch, was "Haskell" zuerst einliest (großes Bild); Typ ist also "außerhalb" in Bezug auf einen Typwert. Die Beziehung zwischen Ebenen (einer Referenzkette) bei der Programmierung ist in der Kategorie nicht einfach zu beschreiben. Die Kategorie des Satzes wird verwendet, um Typen (Int, Strings, Vielleicht Int usw.) zu beschreiben, die die Kategorie des Funktors (parametrisierte Typen) enthalten. Die Referenzkette: Funktortyp, Funktorwerte (Elemente des Funktorsatzes, z. B. Nothing, Just) und alles andere, auf das jeder Funktorwert verweist. In der Kategorie wird die Beziehung anders beschrieben, z. B. return :: a -> m a
wird sie als natürliche Transformation von einem Funktor zu einem anderen Funktor angesehen, anders als alles, was bisher erwähnt wurde.
Zurück zum Hauptthema: Alles in allem beschreibt die Aussage für jedes definierte Tensorprodukt und einen neutralen Wert ein erstaunlich leistungsfähiges Rechenkonstrukt, das aus seiner paradoxen Struktur hervorgeht:
- außen erscheint es als einzelnes Objekt (zB
:: List
); statisch
- aber innen erlaubt viel Dynamik
- Beliebig viele Werte desselben Typs (z. B. leer | ~ nicht leer) als Futter für Funktionen beliebiger Art. Das Tensorprodukt reduziert eine beliebige Anzahl von Eingaben auf einen einzigen Wert ... für die externe Schicht (~
fold
das sagt nichts über die Nutzlast aus)
- unendlicher Bereich sowohl des Typs als auch der Werte für die innerste Schicht
In Haskell ist es wichtig, die Anwendbarkeit der Aussage zu klären. Die Kraft und Vielseitigkeit dieses Konstrukts hat absolut nichts mit einer Monade an sich zu tun . Mit anderen Worten, das Konstrukt beruht nicht darauf, was eine Monade einzigartig macht.
Bei dem Versuch herauszufinden, ob Code mit einem gemeinsamen Kontext erstellt werden soll, um voneinander abhängige Berechnungen zu unterstützen, im Vergleich zu Berechnungen, die parallel ausgeführt werden können, ist diese berüchtigte Aussage, so viel sie beschreibt, kein Kontrast zwischen der Wahl von Anwendbar, Pfeile und Monaden, sondern beschreibt, wie sehr sie gleich sind. Für die vorliegende Entscheidung ist die Aussage umstritten.
Dies wird oft missverstanden. In der Erklärung wird join :: m (m a) -> m a
das Tensorprodukt für den monoidalen Endofunktor beschrieben. Es wird jedoch nicht dargelegt, wie im Rahmen dieser Aussage (<*>)
auch auch gewählt werden könnte. Es ist wirklich ein Beispiel für sechs / ein halbes Dutzend. Die Logik zum Kombinieren von Werten ist genau gleich. Dieselbe Eingabe generiert jeweils die gleiche Ausgabe (im Gegensatz zu den Summen- und Produktmonoiden für Int, da sie beim Kombinieren von Ints unterschiedliche Ergebnisse erzeugen).
Um es noch einmal zusammenzufassen: Ein Monoid in der Kategorie der Endofunktoren beschreibt:
~t :: m * -> m * -> m *
and a neutral value for m *
(<*>)
und (>>=)
beide bieten gleichzeitigen Zugriff auf die beiden m
Werte, um den einzelnen Rückgabewert zu berechnen. Die zur Berechnung des Rückgabewerts verwendete Logik ist genau dieselbe. Ohne die unterschiedlichen Formen der Funktionen, die sie parametrisieren ( f :: a -> b
versus k :: a -> m b
), und die Position des Parameters mit dem gleichen Rückgabetyp der Berechnung (dh a -> b -> b
versus b -> a -> b
für jeden), hätten wir vermutlich die monoidale Logik parametrisieren können, die Tensorprodukt zur Wiederverwendung in beiden Definitionen. Versuchen ~t
Sie als Übung, den Punkt zu verdeutlichen, ihn umzusetzen , und Sie erhalten (<*>)
und (>>=)
hängen davon ab, wie Sie ihn definieren forall a b
.
Wenn mein letzter Punkt konzeptionell mindestens wahr ist, erklärt er den genauen und einzigen rechnerischen Unterschied zwischen Applikativ und Monade: die Funktionen, die sie parametrisieren. Mit anderen Worten, der Unterschied liegt außerhalb der Implementierung dieser Typklassen.
Nach meiner eigenen Erfahrung lieferte Mac Lanes berüchtigtes Zitat ein großartiges "goto" -Mem, ein Wegweiser, auf den ich mich beim Navigieren durch die Kategorie beziehen konnte, um die in Haskell verwendeten Redewendungen besser zu verstehen. Es gelingt ihm, den Umfang einer leistungsstarken Rechenkapazität zu erfassen, die in Haskell wunderbar zugänglich gemacht wird.
Es ist jedoch ironisch, wie ich die Anwendbarkeit der Aussage außerhalb der Monade zum ersten Mal missverstanden habe und was ich hoffe, hier vermittelt zu werden. Alles, was es beschreibt, stellt sich als ähnlich zwischen Applikativ und Monaden (und Pfeilen unter anderem) heraus. Was es nicht sagt, ist genau die kleine, aber nützliche Unterscheidung zwischen ihnen.
- E.