Frage

Kann jemand einige Hinweise auf, warum die unreinen Berechnungen in Haskell als Monaden modelliert?

Ich meine Monade ist nur eine Schnittstelle mit 4 Operationen, also was war die Begründung zur Modellierung Nebenwirkungen in ihm?

War es hilfreich?

Lösung

Angenommen, eine Funktion Nebenwirkungen hat. Wenn wir alle die Auswirkungen nehmen produzieren sie als Eingangs- und Ausgangsparameter, dann ist die Funktion ist rein zur Außenwelt.

Also für eine unreine Funktion

f' :: Int -> Int

Wir fügen Sie den Real World auf die Betrachtung

f :: Int -> RealWorld -> (Int, RealWorld)
-- input some states of the whole world,
-- modify the whole world because of the side effects,
-- then return the new world.

dann ist f wieder rein. Wir definieren eine parametrisierte Datentyp IO a = RealWorld -> (a, RealWorld), so dass wir Real World brauchen nicht zu geben, so viele Male

f :: Int -> IO Int

Um den Programmierer, einen Realworld direkt Handhabung ist zu gefährlich, insbesondere, wenn ein Programmierer ihre Hände auf einem Wert vom Typ Realworld bekommt, könnten sie versuchen, Kopieren es, was im Grunde unmöglich ist. (Denken Sie an versuchen, das gesamte Dateisystem, zum Beispiel zu kopieren. Wo man sagen würde?) Deshalb unsere Definition von IO die Zustände der ganzen Welt kapselt auch.

Diese unreinen Funktionen nutzlos, wenn wir sie nicht zusammen Kette. Betrachten

getLine :: IO String               = RealWorld -> (String, RealWorld)
getContents :: String -> IO String = String -> RealWorld -> (String, RealWorld)
putStrLn :: String -> IO ()        = String -> RealWorld -> ((), RealWorld)

Wir wollen einen Dateinamen aus der Konsole erhalten, diese Datei lesen, drucken Sie dann den Inhalt aus. Wie würden wir es tun, wenn wir die reale Welt Staaten zugreifen kann?

printFile :: RealWorld -> ((), RealWorld)
printFile world0 = let (filename, world1) = getLine world0
                       (contents, world2) = (getContents filename) world1 
                   in  (putStrLn contents) world2 -- results in ((), world3)

Wir sehen hier ein Muster: die Funktionen wie folgt aufgerufen werden:

...
(<result-of-f>, worldY) = f worldX
(<result-of-g>, worldZ) = g <result-of-f> worldY
...

So konnten wir einen Operator ~~~ definieren, sie zu binden:

(~~~) :: (IO b) -> (b -> IO c) -> IO c

(~~~) ::      (RealWorld -> (b, RealWorld))
      -> (b -> RealWorld -> (c, RealWorld))
      ->       RealWorld -> (c, RealWorld)
(f ~~~ g) worldX = let (resF, worldY) = f worldX in
                        g resF worldY

dann könnten wir einfach schreiben

printFile = getLine ~~~ getContents ~~~ putStrLn

ohne die reale Welt zu berühren.


Wir nehmen nun an den Dateiinhalt Groß auch machen wollen. Großgeschrieben ist eine reine Funktion

upperCase :: String -> String

Aber es in der realen Welt zu machen, hat es eine IO String zurückzukehren. Es ist leicht, eine solche Funktion zu heben:

impureUpperCase :: String -> RealWorld -> (String, RealWorld)
impureUpperCase str world = (upperCase str, world)

Dies kann verallgemeinert werden:

impurify :: a -> IO a

impurify :: a -> RealWorld -> (a, RealWorld)
impurify a world = (a, world)

, so dass impureUpperCase = impurify . upperCase, und wir können schreiben

printUpperCaseFile = 
    getLine ~~~ getContents ~~~ (impurify . upperCase) ~~~ putStrLn

(Hinweis: Normalerweise schreiben wir getLine ~~~ getContents ~~~ (putStrLn . upperCase))


Nun wollen wir sehen, was wir getan haben:

  1. Wir definiert einen Operator (~~~) :: IO b -> (b -> IO c) -> IO c die Ketten zwei unreine Funktionen zusammen
  2. Wir definiert eine Funktion impurify :: a -> IO a, die einen reinen Wert unreine umwandelt.

Jetzt machen wir die Identifizierung (>>=) = (~~~) und return = impurify und sehen? Wir haben eine Monade bekommen.


(Um zu überprüfen, ob es wirklich eine Monade gibt es wenige Axiome erfüllt werden:

(1) return a >>= f = f a

  impurify a               = (\world -> (a, world))
 (impurify a ~~~ f) worldX = let (resF, worldY) = (\world -> (a, world)) worldX 
                             in f resF worldY
                           = let (resF, worldY) =            (a, worldX))       
                             in f resF worldY
                           = f a worldX

(2) f >>= return = f

  (f ~~~ impurify) a worldX = let (resF, worldY) = impuify a worldX 
                              in f resF worldY
                            = let (resF, worldY) = (a, worldX)     
                              in f resF worldY
                            = f a worldX

(3) f >>= (\x -> g x >>= h) = (f >>= g) >>= h

Übung.)

Andere Tipps

  

Kann jemand einige Hinweise auf, warum die unreinen Berechnungen in Haskell als Monaden modelliert?

Diese Frage enthält ein weit verbreitetes Missverständnis. Unreinheit und Monad sind unabhängige Vorstellungen. Unreinheit ist nicht modelliert von Monade. Vielmehr gibt es ein paar Datentypen, wie IO, die zwingend notwendig Berechnung darstellen. Und für einige dieser Typen, die als ein winziger Bruchteil ihrer Schnittstelle entspricht dem Interface-Pattern „Monad“. Darüber hinaus wird es keine bekannte reine / functional / denotative Erklärung IO (und es ist unwahrscheinlich zu sein, wenn man die "sin ist" Zweck IO), obwohl es die häufig erzählte Geschichte über World -> (a, World) die Bedeutung von IO a zu sein. Diese Geschichte kann nicht wahrheits beschreiben IO, weil IO Gleichzeitigkeit und Nichtdeterminismus unterstützt. Die Geschichte nicht einmal arbeiten, wenn für deterministische Berechnungen, die Mitte der Berechnung Interaktion mit der Welt ermöglichen.

Für weitere Erläuterungen siehe diese Antwort .

Bearbeiten : Auf die Frage erneut zu lesen, ich glaube nicht, meine Antwort ist ganz auf dem richtigen Weg. Alle Modelle aus Imperativ Berechnung drehen sich oft aus Monaden sein, ebenso wie die Frage, sagte. Der Fragesteller hat vielleicht nicht wirklich davon ausgehen, dass monadness in irgendeiner Weise die Modellierung der imperativen Berechnung ermöglicht.

Wie Sie sagen, ist Monad eine sehr einfache Struktur. Die eine Hälfte der Antwort lautet: Monad die einfachste Struktur ist, dass wir möglicherweise zu Neben Bewirkung Funktionen geben könnte und in der Lage, sie zu nutzen. Mit Monad können wir zwei Dinge tun: können wir einen reinen Wert als Neben Bewirkung Wert (return) behandeln, und wir können eine Neben bewirken Funktion auf einen Neben Bewirkung Wert gelten einen neuen Seite bewirkenden Wert zu erhalten (>>=) . die Fähigkeit zu verlieren, entweder diese Dinge zu tun, lähmende wäre, so dass unsere Seite bewirkende Art Bedürfnisse sein „mindestens“ Monad, und es stellt sich heraus Monad ist genug, um alles zu implementieren wir bisher benötigt haben.

Die andere Hälfte ist: Was ist die detaillierteste Struktur, die wir auf „mögliche Nebenwirkungen“ geben könnten? Wir können sicherlich denken über den Raum aller möglichen Nebenwirkungen als Set (die einzige Operation, die Mitgliedschaft erfordert ist). Wir können zwei Nebenwirkungen kombinieren, indem sie einer nach dem anderen tun, und dies wird zu einer unterschiedlichen Nebenwirkung geben (oder möglicherweise das gleiche - wenn der erste „shutdown Computer“ war und die zweite war „Write-Datei“, dann ist das Ergebnis Komponieren ist diese "shutdown Computer") nur.

Ok, so was kann sagen, dass wir über diesen Vorgang? Es ist assoziativ; Das heißt, wenn wir drei Nebenwirkungen kombinieren, es keine Rolle, in welcher Reihenfolge wir die Kombination in tun. Wenn wir (Write-Datei dann lesen Buchse) dann Shutdown Computer zu tun, es ist die gleiche wie zu tun Write-Datei dann (lesen Buchse dann Abschaltung Computer). Aber es ist nicht kommutativ: ( „Write-Datei“ und dann „Datei löschen“) ist eine andere Nebenwirkung aus ( „Datei löschen“, dann „Write-Datei“). Und wir haben eine Identität: „Gruppe“ die besondere Nebenwirkung „keine Nebenwirkungen“ Werke ( „keine Nebenwirkungen“, dann „Datei löschen“ ist die gleiche Nebenwirkung nur als „Datei löschen“) An dieser Stelle jeder Mathematiker denkt Aber Gruppen haben Umkehrungen, und es gibt keine Möglichkeit, eine Nebenwirkung im Allgemeinen zu invertieren; „Datei löschen“ ist unumkehrbar. So ist die Struktur, die wir verlassen haben, dass ein Monoid ist, die unsere Seite bewirkende Funktionen bedeuten sollten Monaden sein.

Gibt es eine komplexere Struktur? Sicher! Wir konnten teilen mögliche Nebenwirkungen in Dateisystem-basierten Effekte, netzwerkbasierte Effekte und vieles mehr, und wir mit komplizierteren Regeln der Komposition kommen könnten, die diese Details erhalten. Aber auch hier kommt es darauf an: Monad ist sehr einfach, und doch stark genug, um die meisten Eigenschaften zum Ausdruck bringen wir kümmern uns um. (Insbesondere Assoziativität und die anderen Axiome lassen Sie uns in kleine Stücke unserer Anwendung testen, mit Zuversicht, dass die Nebenwirkungen der kombinierten Anwendung wird die gleiche wie die Kombination der Nebenwirkungen der Stücke sein).

Es ist eigentlich ganz eine saubere Art und Weise von I / O in einer funktionalen Weise zu denken.

In den meisten Programmiersprachen, was Sie tun Ein- / Ausgabeoperationen. In Haskell, vorstellen, das Schreiben von Code nicht zu Sie die Operationen, sondern eine Liste der Operationen zu erzeugen, dass Sie tun würde.

Monaden sind nur ziemlich Syntax für genau das.

Wenn Sie wissen wollen, warum als Monaden auf etwas anderes im Gegensatz ich die Antwort erraten ist, dass sie die beste funktionelle Weise, die ich zu vertreten / O, dass die Leute denken könnten, wenn sie Haskell wurden zu machen.

AFAIK, ist der Grund der Lage sein, um Nebenwirkungen zu Kontrollen in dem Typsystem zu umfassen. Wenn Sie mehr wissen wollen, hören diejenigen SE-Radio- Folgen: Episode 108: Simon Peyton Jones auf Functional Programming und Haskell Episode 72: Erik Meijer auf LINQ

Vor gibt es sehr gute ausführliche Antworten mit theoretischem Hintergrund. Aber ich möchte, dass meine Sicht auf IO Monade geben. Ich bin nicht Haskell Programmierer erfahren, so sein kann es ganz naiv oder sogar falsch ist. Aber ich half mir mit IO umgehen zu einem gewissen Grad Monade (beachten Sie, dass es nicht auf andere Monaden Sie bezieht).

Zuerst möchte ich sagen, dass zB mit „realen Welt“ ist für mich nicht zu löschen, da wir nicht seine (reale Welt) frühere Zustände zugreifen können. sein kann, es bezieht sich nicht Berechnungen überhaupt Monade, aber es ist im Sinne der Referenz Transparenz gewünscht wird, die in der Regel präsentiert in Haskell-Code ist.

So wir unsere Sprache (Haskell) wollen rein. Aber wir brauchen Ein- / Ausgabeoperationen als ohne sie unser Programm nicht nützlich sein kann. Und diese Vorgänge können nicht ihrer Natur nach rein sein. So ist die einzige Art und Weise damit umgehen müssen wir getrennte unreine Operationen vom Rest des Codes.

Hier Monade kommt. Eigentlich bin ich nicht sicher, dass es nicht anderes Konstrukt existieren kann mit ähnlichen Eigenschaften benötigt, aber der Punkt ist, dass diese Eigenschaften Monade hat, so kann es (und es wird verwendet, erfolgreich) verwendet werden. Die wichtigste Eigenschaft ist, dass wir nicht von ihm entweichen kann. Monad-Schnittstelle hat keine Operationen rund um unseren Wert loswerden die Monade zu bekommen. Andere (nicht IO) Monaden bieten solche Operationen und ermöglichen Musteranpassung (z.B. vielleicht), aber diese Operationen sind nicht in monadisch-Schnittstelle. Eine weitere erforderliche Eigenschaft ist die Fähigkeit zu Chain-Operationen.

Wenn wir darüber nachdenken, was wir brauchen in Bezug auf die Art System, kommen wir zu der Tatsache, dass wir mit Konstruktor eingeben müssen, die um jeden vale gewickelt werden kann. Constructor muss privat sein, wie wir verbieten es zu entkommen (d. Pattern-Matching). Aber wir brauchen Funktion Put-Wert in diesem Konstruktor (hier Rückkehr in den Sinn kommt). Und wir müssen den Weg zu Chain-Operationen. Wenn wir darüber einige Zeit denken, werden wir darauf kommen müssen, dass Verkettungsoperationstyp wie haben >> = hat. So kommen wir zu etwas sehr ähnlich Monade. Ich denke, wenn wir jetzt möglich widersprüchlichen Situationen mit diesem Konstrukt analysieren, kommen wir Axiome Monade.

Beachten Sie, dass die entwickelten Konstrukt hat nichts gemein mit Verunreinigung. Es hat nur Eigenschaften, die wir wünschten Lage sein zu müssen mit unreinen Operationen zu behandeln, und zwar ohne zu entkommen, verketten und eine Art und Weise zu bekommen.

Jetzt ist einige Sätze von unreinen Operationen werden durch die Sprache innerhalb dieser ausgewählten Monade IO vorgegeben. Wir können diese Operationen kombinieren, um neue unreine Operationen zu erstellen. Und all diese Operationen haben IO in ihrer Art haben. Beachten Sie jedoch, dass Vorhandensein von IO in Art einer Funktion diese Funktion nicht unrein machen. Aber wie ich sie verstehe, ist es keine gute Idee reine Funktionen mit IO in ihrer Art zu schreiben, wie es zunächst unsere Idee war, reinen und unreinen Funktionen zu trennen.

Schließlich möchte ich sagen, dass unreine Monade schalte keine Operationen in Reinen. Es erlaubt nur sie effektiv zu trennen. (Ich wiederhole, dass es nur mein Verständnis ist)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top