Könnte jemand einige Hinweise geben, warum die unreinen Berechnungen in Haskell als Monaden modelliert werden?
Ich meine, Monade ist nur eine Schnittstelle mit 4 Operationen. Was war also der Grund für die Modellierung von Nebenwirkungen?
haskell
functional-programming
monads
bodacydo
quelle
quelle
return
und(>>=)
.x >> y
ist dasselbe wiex >>= \\_ -> y
(dh es ignoriert das Ergebnis des ersten Arguments). Wir reden nicht darüberfail
.fail
istMonad
wegen eines historischen Unfalls in der Klasse; es gehört wirklich inMonadPlus
. Beachten Sie, dass die Standarddefinition unsicher ist.Antworten:
Angenommen, eine Funktion hat Nebenwirkungen. Wenn wir alle Effekte, die es erzeugt, als Eingabe- und Ausgabeparameter verwenden, ist die Funktion für die Außenwelt rein.
Also für eine unreine Funktion
Wir fügen der Betrachtung die RealWorld hinzu
dann
f
ist wieder rein. Wir definieren einen parametrisierten Datentyptype IO a = RealWorld -> (a, RealWorld)
, sodass wir RealWorld nicht so oft eingeben müssen und nur schreiben könnenFür den Programmierer ist die direkte Handhabung einer RealWorld zu gefährlich. Insbesondere wenn ein Programmierer einen Wert vom Typ RealWorld in die Hände bekommt, versucht er möglicherweise, ihn zu kopieren , was im Grunde unmöglich ist. (Denken Sie beispielsweise daran, das gesamte Dateisystem zu kopieren. Wo würden Sie es ablegen?) Daher umfasst unsere Definition von E / A auch die Zustände der ganzen Welt.
Zusammensetzung "unreiner" Funktionen
Diese unreinen Funktionen sind nutzlos, wenn wir sie nicht miteinander verketten können. Erwägen
Wir wollen
Wie würden wir es tun, wenn wir Zugang zu den Staaten der realen Welt hätten?
Wir sehen hier ein Muster. Die Funktionen heißen folgendermaßen:
Wir könnten also einen Operator definieren
~~~
, um sie zu binden:dann könnten wir einfach schreiben
ohne die reale Welt zu berühren.
"Verunreinigung"
Angenommen, wir möchten den Dateiinhalt auch in Großbuchstaben schreiben. Großbuchstaben sind eine reine Funktion
Aber um es in die reale Welt zu schaffen, muss es eine zurückgeben
IO String
. Es ist einfach, eine solche Funktion aufzuheben:Dies kann verallgemeinert werden:
so dass
impureUpperCase = impurify . upperCase
, und wir können schreiben(Hinweis: Normalerweise schreiben wir
getLine ~~~ getContents ~~~ (putStrLn . upperCase)
)Wir haben die ganze Zeit mit Monaden gearbeitet
Nun wollen wir sehen, was wir getan haben:
(~~~) :: IO b -> (b -> IO c) -> IO c
der zwei unreine Funktionen miteinander verkettetimpurify :: a -> IO a
, die einen reinen Wert in unrein umwandelt.Jetzt machen wir die Identifizierung
(>>=) = (~~~)
undreturn = impurify
, und sehen? Wir haben eine Monade.Technischer Hinweis
Um sicherzustellen, dass es sich wirklich um eine Monade handelt, müssen noch einige Axiome überprüft werden:
return a >>= f = f a
f >>= return = f
f >>= (\x -> g x >>= h) = (f >>= g) >>= h
Links als Übung.
quelle
RealWorld
in ... na ja , Sie werden sehen.IO
, da letztere Interaktion, Parallelität und Nichtdeterminismus unterstützt. Weitere Hinweise finden Sie in meiner Antwort auf diese Frage.IO
diese Weise,RealWorld
repräsentiert jedoch nicht die reale Welt. Es ist nur ein Zeichen, um die Operationen in Ordnung zu halten (die "Magie" ist, dassRealWorld
GHC Haskells einziger Einzigartigkeitstyp ist)IO
eine Kombination aus dieser Darstellung und nicht standardmäßiger Compilermagie (die an den berühmten C-Compiler-Virus von Ken Thompson erinnert ). Bei anderen Typen liegt die Wahrheit im Quellcode zusammen mit der üblichen Haskell-Semantik.Diese Frage enthält ein weit verbreitetes Missverständnis. Unreinheit und Monade sind unabhängige Begriffe. Verunreinigungen werden von Monad nicht modelliert. Vielmehr gibt es einige Datentypen, die beispielsweise eine
IO
zwingende Berechnung darstellen. Und für einige dieser Typen entspricht ein winziger Bruchteil ihrer Schnittstelle dem Schnittstellenmuster "Monad". Darüber hinaus ist keine reine / funktionale / denotative Erklärung bekanntIO
(und es ist unwahrscheinlich, dass es eine gibt, wenn man den Zweck von "sin bin" berücksichtigtIO
), obwohl es die allgemein erzählte Geschichte überWorld -> (a, World)
die Bedeutung von gibtIO a
. Diese Geschichte kann nicht wahrheitsgemäß beschreibenIO
, weilIO
unterstützt Parallelität und Nichtdeterminismus. Die Geschichte funktioniert nicht einmal bei deterministischen Berechnungen, die eine Interaktion mit der Welt während der Berechnung ermöglichen.Weitere Erklärungen finden Sie in dieser Antwort .
Bearbeiten : Beim erneuten Lesen der Frage denke ich nicht, dass meine Antwort ganz auf dem richtigen Weg ist. Modelle der imperativen Berechnung erweisen sich oft als Monaden, genau wie die Frage sagte. Der Fragesteller geht möglicherweise nicht wirklich davon aus, dass Monadität in irgendeiner Weise die Modellierung imperativer Berechnungen ermöglicht.
quelle
RealWorld
ist, wie die Dokumente sagen, "zutiefst magisch". Es ist ein Token, das darstellt, was das Laufzeitsystem tut. Es bedeutet eigentlich nichts über die reale Welt. Sie können nicht einmal einen neuen heraufbeschwören, um einen "Thread" zu erstellen, ohne zusätzliche Tricks zu machen. Der naive Ansatz würde nur eine einzige blockierende Aktion mit viel Unklarheit darüber erzeugen, wann sie ausgeführt wird.Monad
im Allgemeinen wirklich.Monad
im Allgemeinen" meine ich grobforall m. Monad m => ...
, dh an einer beliebigen Instanz arbeiten. Die Dinge, die Sie mit einer beliebigen Monade tun können, sind fast genau die gleichen Dinge, mit denen Sie tun könnenIO
: undurchsichtige Grundelemente (als Funktionsargumente bzw. aus Bibliotheken) empfangen, No-Ops mit erstellenreturn
oder einen Wert auf irreversible Weise mit transformieren(>>=)
. Die Essenz der Programmierung in einer beliebigen Monade besteht darin, eine Liste unwiderruflicher Aktionen zu erstellen: "mache X, dann mache Y, dann ...". Klingt für mich ziemlich zwingend!m
könnte es hilfreicher sein, als existentiell zu denken . Darüber hinaus ist meine "Interpretation" eine Neuformulierung der Gesetze; Die Liste der "do X" -Anweisungen ist genau das freie Monoid auf der unbekannten Struktur, die über erstellt wurde(>>=)
. und die Monadengesetze sind nur monoide Gesetze zur Endofunktorkomposition.IO
ist ein pathologischer Fall, gerade weil er fast nichts mehr als dieses Minimum bietet . In bestimmten Fällen können Typen mehr Struktur aufweisen und somit eine tatsächliche Bedeutung haben. Ansonsten stehen die wesentlichen Eigenschaften einer Monade - basierend auf den Gesetzen - der eindeutigen Bezeichnung ebenso entgegen wie sieIO
ist. Ohne den Export von Konstruktoren, die erschöpfende Aufzählung primitiver Aktionen oder ähnliches ist die Situation hoffnungslos.So wie ich es verstehe, bemerkte jemand namens Eugenio Moggi zuerst, dass ein zuvor obskures mathematisches Konstrukt namens "Monade" verwendet werden könnte, um Nebenwirkungen in Computersprachen zu modellieren und daher ihre Semantik mithilfe der Lambda-Rechnung zu spezifizieren. Als Haskell entwickelt wurde, gab es verschiedene Möglichkeiten, unreine Berechnungen zu modellieren (siehe Simon Peyton Jones ' "Haarhemd" -Papier für weitere Details), aber als Phil Wadler Monaden einführte, wurde schnell klar, dass dies die Antwort war. Und der Rest ist Geschichte.
quelle
Nun, weil Haskell rein ist . Sie benötigen ein mathematisches Konzept zu unterscheiden zwischen unreinen Berechnungen und Reinen auf Typ-Ebene und zum Modellprogramm fließt jeweils in.
Dies bedeutet, dass Sie am Ende einen Typ haben müssen
IO a
, der eine unreine Berechnung modelliert. Dann müssen Sie wissen, wie Sie diese Berechnungen kombinieren können, die in Sequenz (>>=
) gelten und einen Wert (return
) anheben. Dies sind die offensichtlichsten und grundlegendsten.Mit diesen beiden haben Sie bereits eine Monade definiert (ohne daran zu denken);)
Darüber hinaus bieten Monaden sehr allgemein und mächtige Abstraktionen , können so viele Arten von Steuerfluss bequem in monadischen Funktionen verallgemeinert werden , wie
sequence
,liftM
oder spezielle Syntax, unpureness nicht so einen Sonderfall machen.Weitere Informationen finden Sie unter Monaden in funktionaler Programmierung und Eindeutigkeitstypisierung (die einzige mir bekannte Alternative).
quelle
Wie Sie sagen,
Monad
ist eine sehr einfache Struktur. Die eine Hälfte der Antwort lautet:Monad
ist die einfachste Struktur, die wir möglicherweise nebenwirkenden Funktionen geben und sie verwenden können. MitMonad
können wir zwei Dinge tun: Wir können einen reinen Wert als Nebenwirkungswert behandeln (return
), und wir können eine Nebenwirkungsfunktion auf einen Nebenwirkungswert anwenden, um einen neuen Nebenwirkungswert zu erhalten (>>=
). Die Fähigkeit zu verlieren, eines dieser Dinge zu tun, wäre lähmend, daher muss unser Nebeneffekttyp "mindestens" seinMonad
, und es stellt sich heraus, dassMonad
es ausreicht, alles zu implementieren, was wir bisher benötigt haben.Die andere Hälfte ist: Was ist die detaillierteste Struktur, die wir "möglichen Nebenwirkungen" geben könnten? Wir können uns den Raum aller möglichen Nebenwirkungen durchaus als Satz vorstellen (die einzige Operation, die erforderlich ist, ist die Mitgliedschaft). Wir können zwei Nebenwirkungen kombinieren, indem wir sie nacheinander ausführen. Dies führt zu einer anderen Nebenwirkung (oder möglicherweise zur gleichen - wenn die erste "Computer herunterfahren" und die zweite "Datei schreiben" war, dann das Ergebnis diese zu komponieren ist nur "Computer herunterfahren").
Ok, was können wir zu dieser Operation sagen? Es ist assoziativ; Das heißt, wenn wir drei Nebenwirkungen kombinieren, spielt es keine Rolle, in welcher Reihenfolge wir das Kombinieren durchführen. Wenn wir dies tun (Datei schreiben, dann Socket lesen) und dann den Computer herunterfahren, ist es dasselbe wie dann eine Datei schreiben (Socket lesen, dann herunterfahren) Computer). Aber es ist nicht kommutativ: ("Datei schreiben", dann "Datei löschen") ist ein anderer Nebeneffekt als ("Datei löschen", dann "Datei schreiben"). Und wir haben eine Identität: Der spezielle Nebeneffekt "keine Nebenwirkungen" funktioniert ("keine Nebenwirkungen", dann "Datei löschen" ist der gleiche Nebeneffekt wie nur "Datei löschen"). An diesem Punkt denkt jeder Mathematiker "Gruppe!" Aber Gruppen haben Umkehrungen, und es gibt keine Möglichkeit, eine Nebenwirkung im Allgemeinen umzukehren. "Datei löschen" ist irreversibel. Die Struktur, die wir übrig haben, ist die eines Monoids, was bedeutet, dass unsere Nebenwirkungen Monaden sein sollten.
Gibt es eine komplexere Struktur? Sicher! Wir könnten mögliche Nebenwirkungen in dateisystembasierte Effekte, netzwerkbasierte Effekte und mehr unterteilen und ausgefeiltere Kompositionsregeln entwickeln, die diese Details bewahren. Aber es kommt wieder darauf an:
Monad
ist sehr einfach und dennoch leistungsfähig genug, um die meisten Eigenschaften auszudrücken, die uns wichtig sind. (Insbesondere die Assoziativität und die anderen Axiome lassen uns unsere Anwendung in kleinen Stücken testen, mit der Gewissheit, dass die Nebenwirkungen der kombinierten Anwendung mit der Kombination der Nebenwirkungen der Teile übereinstimmen).quelle
Es ist eigentlich eine ziemlich saubere Art, E / A auf funktionale Weise zu betrachten.
In den meisten Programmiersprachen führen Sie Eingabe- / Ausgabeoperationen aus. In Haskell, das Schreiben von Code vorstellen , nicht zu tun , die Operationen, sondern eine Liste der Operationen zu erzeugen , die Sie tun mögen.
Monaden sind einfach eine hübsche Syntax für genau das.
Wenn Sie wissen möchten, warum Monaden im Gegensatz zu etwas anderem stehen, ist die Antwort wahrscheinlich, dass sie die beste funktionale Art sind, E / A darzustellen, an die die Leute denken konnten, als sie Haskell machten.
quelle
AFAIK, der Grund ist, Nebenwirkungsprüfungen in das Typensystem aufnehmen zu können. Wenn Sie mehr wissen möchten, hören Sie sich diese SE-Radio- Episoden an: Episode 108: Simon Peyton Jones über funktionale Programmierung und Haskell Episode 72: Erik Meijer über LINQ
quelle
Oben finden Sie sehr gute detaillierte Antworten mit theoretischem Hintergrund. Aber ich möchte meine Meinung zur IO-Monade äußern. Ich bin kein erfahrener Haskell-Programmierer, also kann es sein, dass es ziemlich naiv oder sogar falsch ist. Aber ich habe mir geholfen, bis zu einem gewissen Grad mit IO-Monaden umzugehen (beachten Sie, dass sie sich nicht auf andere Monaden beziehen).
Zunächst möchte ich sagen, dass dieses Beispiel mit "realer Welt" für mich nicht allzu klar ist, da wir nicht auf seine (realen) früheren Zustände zugreifen können. Möglicherweise bezieht es sich überhaupt nicht auf Monadenberechnungen, aber es ist im Sinne einer referenziellen Transparenz erwünscht, die im Allgemeinen im Haskell-Code enthalten ist.
Wir wollen also, dass unsere Sprache (haskell) rein ist. Aber wir brauchen Eingabe- / Ausgabeoperationen, da unser Programm ohne sie nicht nützlich sein kann. Und diese Operationen können von Natur aus nicht rein sein. Der einzige Weg, um damit umzugehen, besteht darin, unreine Operationen vom Rest des Codes zu trennen.
Hier kommt die Monade. Eigentlich bin ich mir nicht sicher, ob es kein anderes Konstrukt mit ähnlich benötigten Eigenschaften geben kann, aber der Punkt ist, dass Monaden diese Eigenschaften haben, damit sie verwendet werden können (und erfolgreich verwendet werden). Die Haupteigenschaft ist, dass wir nicht davon entkommen können. Die Monadenschnittstelle hat keine Operationen, um die Monade um unseren Wert herum loszuwerden. Andere (nicht E / A-) Monaden stellen solche Operationen bereit und ermöglichen einen Mustervergleich (z. B. Vielleicht), aber diese Operationen befinden sich nicht in der Monadenschnittstelle. Eine weitere erforderliche Eigenschaft ist die Fähigkeit, Operationen zu verketten.
Wenn wir darüber nachdenken, was wir in Bezug auf das Typsystem benötigen, kommen wir zu der Tatsache, dass wir einen Typ mit Konstruktor benötigen, der um jedes Tal gewickelt werden kann. Der Konstruktor muss privat sein, da wir das Entkommen aus ihm verbieten (dh Mustervergleich). Aber wir brauchen eine Funktion, um diesem Konstruktor einen Wert zu verleihen (hier fällt uns die Rückkehr ein). Und wir brauchen den Weg zur Verkettung. Wenn wir einige Zeit darüber nachdenken, werden wir zu der Tatsache kommen, dass die Verkettungsoperation den Typ >> = haben muss. Wir kommen also zu etwas, das der Monade sehr ähnlich ist. Ich denke, wenn wir jetzt mögliche widersprüchliche Situationen mit diesem Konstrukt analysieren, werden wir zu Monadenaxiomen kommen.
Beachten Sie, dass das entwickelte Konstrukt nichts mit Verunreinigungen gemein hat. Es hat nur Eigenschaften, die wir haben wollten, um mit unreinen Operationen umgehen zu können, nämlich nicht entkommen, verketten und einen Weg, um hineinzukommen.
Nun wird eine Reihe von unreinen Operationen durch die Sprache innerhalb dieser ausgewählten Monaden-E / A vordefiniert. Wir können diese Operationen kombinieren, um neue unreine Operationen zu erstellen. Und all diese Operationen müssen E / A in ihrem Typ haben. Beachten Sie jedoch, dass das Vorhandensein von E / A in der Art einer Funktion diese Funktion nicht unrein macht. Aber so wie ich es verstehe, ist es eine schlechte Idee, reine Funktionen mit IO in ihrem Typ zu schreiben, da es ursprünglich unsere Idee war, reine und unreine Funktionen zu trennen.
Abschließend möchte ich sagen, dass Monaden unreine Operationen nicht in reine verwandeln. Es erlaubt nur, sie effektiv zu trennen. (Ich wiederhole, dass es nur mein Verständnis ist)
quelle