Um schnelle Leser zu respektieren, beginne ich zuerst mit einer genauen Definition, fahre mit einer schnelleren "einfacheren englischen" Erklärung fort und gehe dann zu Beispielen über.
Hier ist eine präzise und präzise Definition, die leicht umformuliert wurde:
Eine Monade (in der Informatik) ist formal eine Karte, die:
sendet jeden Typ X
einer bestimmten Programmiersprache an einen neuen Typ T(X)
(der als "Typ von T
Berechnungen mit Werten in X
" bezeichnet wird);
ausgestattet mit einer Regel zum Zusammensetzen von zwei Funktionen des Formulars
f:X->T(Y)
und g:Y->T(Z)
einer Funktion g∘f:X->T(Z)
;
auf eine Weise, die im offensichtlichen Sinne assoziativ und in Bezug auf eine gegebene Einheitsfunktion unital pure_X:X->T(X)
ist, die als Wert für die reine Berechnung angesehen wird, die diesen Wert einfach zurückgibt.
In einfachen Worten, eine Monade ist eine Regel, die von einem beliebigen Typ X
an einen anderen Typ übergeben werden mussT(X)
, und eine Regel, die von zwei Funktionen f:X->T(Y)
und g:Y->T(Z)
(die Sie komponieren möchten, aber nicht können) an eine neue Funktion übergeben werden kannh:X->T(Z)
. Was jedoch nicht die Zusammensetzung im strengen mathematischen Sinne ist. Wir "biegen" im Grunde die Zusammensetzung der Funktion oder definieren neu, wie Funktionen zusammengesetzt sind.
Außerdem benötigen wir die Kompositionsregel der Monade, um die "offensichtlichen" mathematischen Axiome zu erfüllen:
- Assoziativität : Das Komponieren
f
mit g
und dann mit h
(von außen) sollte dasselbe sein wie das Komponieren g
mit h
und dann mit f
(von innen).
- Unitales Eigentum : Das Komponieren
f
mit der Identitätsfunktion auf beiden Seiten sollte ergeben f
.
Mit einfachen Worten, wir können nicht einfach verrückt werden, wenn wir unsere Funktionszusammensetzung neu definieren, wie wir möchten:
- Wir brauchen zuerst die Assoziativität, um mehrere Funktionen in einer Reihe zusammensetzen zu können, z. B.
f(g(h(k(x)))
und um die Reihenfolge der Funktionspaare nicht zusammensetzen zu müssen. Da die Monadenregel nur vorschreibt, wie ein Funktionspaar ohne dieses Axiom zusammengesetzt werden soll, müssten wir wissen, welches Paar zuerst zusammengesetzt wird und so weiter. (Beachten Sie, dass sich dies von der Kommutativitätseigenschaft unterscheidet, f
mit g
der g
zusammengesetzt wurde f
, die mit der zusammengesetzten Eigenschaft identisch ist , was nicht erforderlich ist.)
- Und zweitens brauchen wir das einheitliche Eigentum, das heißt einfach, dass Identitäten trivial so zusammengesetzt sind, wie wir sie erwarten. So können wir Funktionen sicher umgestalten, wenn diese Identitäten extrahiert werden können.
Also noch einmal kurz: Eine Monade ist die Regel der Typerweiterung und der Kompositionsfunktionen, die die beiden Axiome - Assoziativität und unitales Eigentum - erfüllen.
In der Praxis möchten Sie, dass die Monade von der Sprache, dem Compiler oder dem Framework für Sie implementiert wird, die sich um das Erstellen von Funktionen für Sie kümmern. Sie können sich also darauf konzentrieren, die Logik Ihrer Funktion zu schreiben, anstatt sich Gedanken darüber zu machen, wie ihre Ausführung implementiert wird.
Das ist es im Wesentlichen, kurz gesagt.
Als professioneller Mathematiker vermeide ich es lieber, h
die "Komposition" von f
und zu nennen g
. Weil es mathematisch nicht so ist. Wenn man es die "Komposition" nennt, h
wird fälschlicherweise davon ausgegangen, dass es sich um die wahre mathematische Komposition handelt, die es nicht ist. Es wird nicht einmal eindeutig von f
und bestimmt g
. Stattdessen ist es das Ergebnis der neuen "Regel" unserer Monade, die Funktionen zusammenzusetzen. Was sich von der tatsächlichen mathematischen Zusammensetzung völlig unterscheiden kann, selbst wenn letztere existiert!
Um es weniger trocken zu machen, möchte ich versuchen, es anhand eines Beispiels zu veranschaulichen, das ich mit kleinen Abschnitten kommentiere, damit Sie direkt zum Punkt springen können.
Ausnahme werfen als Monadenbeispiele
Angenommen, wir möchten zwei Funktionen zusammensetzen:
f: x -> 1 / x
g: y -> 2 * y
Ist f(0)
aber nicht definiert, wird eine Ausnahme e
ausgelöst. Wie können Sie dann den Kompositionswert definieren g(f(0))
? Wirf natürlich wieder eine Ausnahme! Vielleicht das gleiche e
. Möglicherweise eine neue aktualisierte Ausnahme e1
.
Was genau passiert hier? Erstens benötigen wir neue Ausnahmewerte (unterschiedlich oder gleich). Sie können sie nothing
oder null
was auch immer nennen, aber die Essenz bleibt dieselbe - sie sollten neue Werte sein, z. B. sollte es number
in unserem Beispiel hier kein sein . Ich ziehe es vor, sie nicht anzurufen null
, um Verwechslungen mit null
der Implementierung in einer bestimmten Sprache zu vermeiden . Ebenso bevorzuge ich es zu vermeiden, nothing
weil es oft damit verbunden ist null
, was im Prinzip zu null
tun ist, aber dieses Prinzip wird oft aus welchen praktischen Gründen auch immer verbogen.
Was genau ist eine Ausnahme?
Dies ist für jeden erfahrenen Programmierer eine triviale Angelegenheit, aber ich möchte ein paar Worte fallen lassen, um jeden Wurm der Verwirrung auszulöschen:
Ausnahme ist ein Objekt, das Informationen darüber enthält, wie das ungültige Ergebnis der Ausführung aufgetreten ist.
Dies kann vom Wegwerfen von Details und Zurückgeben eines einzelnen globalen Werts (wie NaN
oder null
) oder vom Generieren einer langen Protokollliste oder was genau passiert ist, vom Senden an eine Datenbank und Replizieren über die gesamte verteilte Datenspeicherschicht reichen;)
Der wichtige Unterschied zwischen diesen beiden extremen Ausnahmebeispielen besteht darin, dass im ersten Fall keine Nebenwirkungen auftreten . In der zweiten gibt es. Das bringt uns zur (Tausend-Dollar-) Frage:
Sind Ausnahmen in reinen Funktionen erlaubt?
Kürzere Antwort : Ja, aber nur, wenn sie nicht zu Nebenwirkungen führen.
Längere Antwort. Um rein zu sein, muss die Ausgabe Ihrer Funktion eindeutig durch ihre Eingabe bestimmt werden. Deshalb ändern wir unsere Funktion, f
indem wir 0
an den neuen abstrakten Wert senden , den e
wir Ausnahme nennen. Wir stellen sicher, dass der Wert e
keine externen Informationen enthält, die nicht eindeutig durch unsere Eingabe bestimmt werden x
. Hier ist ein Beispiel für eine Ausnahme ohne Nebenwirkung:
e = {
type: error,
message: 'I got error trying to divide 1 by 0'
}
Und hier ist einer mit Nebeneffekt:
e = {
type: error,
message: 'Our committee to decide what is 1/0 is currently away'
}
Tatsächlich hat es nur dann Nebenwirkungen, wenn sich diese Meldung möglicherweise in Zukunft ändern kann. Wenn sich jedoch garantiert nie ändert, wird dieser Wert eindeutig vorhersehbar, sodass keine Nebenwirkungen auftreten.
Um es noch dümmer zu machen. Eine Funktion, die 42
jemals zurückkehrt , ist eindeutig rein. Aber wenn jemand Verrückter beschließt, 42
eine Variable zu erstellen, deren Wert sich möglicherweise ändert, ist dieselbe Funktion unter den neuen Bedingungen nicht mehr rein.
Beachten Sie, dass ich der Einfachheit halber die Objektliteralnotation verwende, um das Wesentliche zu demonstrieren. Leider sind die Dinge in Sprachen wie JavaScript durcheinander, wo error
es keinen Typ gibt, der sich in Bezug auf die Funktionszusammensetzung so verhält, wie wir es hier wollen, während tatsächliche Typen sich so verhalten null
oder NaN
nicht so verhalten, sondern eher künstlich und nicht immer intuitiv sind Typkonvertierungen.
Typerweiterung
Da wir die Nachricht innerhalb unserer Ausnahme variieren möchten, deklarieren wir wirklich einen neuen Typ E
für das gesamte Ausnahmeobjekt und dann ist es das maybe number
, was das tut, abgesehen von seinem verwirrenden Namen, der entweder vom Typ number
oder vom neuen Ausnahmetyp sein soll E
Es ist also wirklich die Vereinigung number | E
von number
und E
. Insbesondere hängt es davon ab, wie wir konstruieren wollen E
, was im Namen weder vorgeschlagen noch reflektiert wird maybe number
.
Was ist funktionale Zusammensetzung?
Es ist die mathematische Operation Mitnahmen Funktionen
f: X -> Y
und g: Y -> Z
und deren Zusammensetzung als Funktion der Konstruktion h: X -> Z
erfüllen h(x) = g(f(x))
. Das Problem mit dieser Definition tritt auf, wenn das Ergebnis f(x)
als Argument von nicht zulässig ist g
.
In der Mathematik können diese Funktionen nicht ohne zusätzliche Arbeit komponiert werden. Die streng mathematische Lösung für unser obiges Beispiel von f
und g
besteht darin, 0
aus dem Definitionssatz von zu entfernen f
. Mit dieser neuen Definition (neue restriktivere Art von x
) f
wird mit zusammensetzbar g
.
Bei der Programmierung ist es jedoch nicht sehr praktisch, den Satz solcher Definitionen einzuschränken f
. Stattdessen können Ausnahmen verwendet werden.
Oder wie ein anderer Ansatz sind künstliche Werte geschaffen wie NaN
, undefined
, null
, Infinity
etc. So können Sie bewerten 1/0
zu Infinity
und 1/-0
zu -Infinity
. Und dann zwingen Sie den neuen Wert zurück in Ihren Ausdruck, anstatt eine Ausnahme auszulösen. Zu Ergebnissen führen, die Sie möglicherweise vorhersehbar finden oder nicht:
1/0 // => Infinity
parseInt(Infinity) // => NaN
NaN < 0 // => false
false + 1 // => 1
Und wir sind zurück zu regulären Zahlen, die bereit sind, weiterzumachen;)
Mit JavaScript können wir numerische Ausdrücke um jeden Preis ausführen, ohne Fehler wie im obigen Beispiel zu verursachen. Das heißt, es ermöglicht auch das Zusammenstellen von Funktionen. Genau darum geht es in der Monade - es ist eine Regel, Funktionen zu komponieren, die den zu Beginn dieser Antwort definierten Axiomen entsprechen.
Aber ist die Regel der Kompositionsfunktion, die sich aus der Implementierung von JavaScript für den Umgang mit numerischen Fehlern ergibt, eine Monade?
Um diese Frage zu beantworten, müssen Sie nur die Axiome überprüfen (als Übung als Teil der Frage hier belassen;).
Kann eine Wurfausnahme verwendet werden, um eine Monade zu konstruieren?
In der Tat wäre eine nützlichere Monade stattdessen die Regel, die vorschreibt, dass, wenn f
für einige eine Ausnahme ausgelöst wird x
, dies auch für die Zusammensetzung bei einigen gilt g
. Machen Sie die Ausnahme E
mit nur einem möglichen Wert ( Terminalobjekt in der Kategorietheorie) global eindeutig . Jetzt sind die beiden Axiome sofort überprüfbar und wir erhalten eine sehr nützliche Monade. Und das Ergebnis ist die sogenannte Monade .