haskell - Was ist eine Monade?

Translate

Nachdem ich Haskell kürzlich kurz angeschaut hatte, was wäre das?kurz, prägnant, praktischErklärung, was eine Monade im Wesentlichen ist?

Ich habe festgestellt, dass die meisten Erklärungen, auf die ich gestoßen bin, ziemlich unzugänglich sind und keine praktischen Details enthalten.

This question and all comments follow the "Attribution Required."

Alle Antworten

Translate

Erstens: Der BegriffMonadeist ein bisschen leer, wenn Sie kein Mathematiker sind. Ein alternativer Begriff istBerechnungs-BuilderDas ist etwas aussagekräftiger für das, wofür sie tatsächlich nützlich sind.

Sie fragen nach praktischen Beispielen:

Beispiel 1: Listenverständnis:

[x*2 | x<-[1..10], odd x]

Dieser Ausdruck gibt das Doppel aller ungeraden Zahlen im Bereich von 1 bis 10 zurück. Sehr nützlich!

Es stellt sich heraus, dass dies für einige Operationen innerhalb der List-Monade wirklich nur syntaktischer Zucker ist. Das gleiche Listenverständnis kann geschrieben werden als:

do
   x <- [1..10]
   guard (odd x)
   return (x * 2)

Oder auch:

[1..10] >>= (\x -> guard (odd x) >> return (x*2))

Beispiel 2: Eingabe / Ausgabe:

do
   putStrLn "What is your name?"
   name <- getLine
   putStrLn ("Welcome, " ++ name ++ "!")

Beide Beispiele verwenden Monaden, AKA-Berechnungsgeneratoren. Das gemeinsame Thema ist, dass die MonadeKettenoperationenauf eine bestimmte, nützliche Weise. Im Listenverständnis sind die Operationen so verkettet, dass, wenn eine Operation eine Liste zurückgibt, die folgenden Operationen ausgeführt werdenjeder ArtikelIn der Liste. Die E / A-Monade hingegen führt die Operationen nacheinander aus, gibt jedoch eine "versteckte Variable" weiter, die den "Zustand der Welt" darstellt, wodurch wir E / A-Code auf rein funktionale Weise schreiben können.

Es stellt sich das Muster von herausVerkettungsvorgängeist sehr nützlich und wird für viele verschiedene Dinge in Haskell verwendet.

Ein weiteres Beispiel sind Ausnahmen: Verwenden derErrorMonade, Operationen werden so verkettet, dass sie nacheinander ausgeführt werden, außer wenn ein Fehler ausgelöst wird. In diesem Fall wird der Rest der Kette abgebrochen.

Sowohl die Syntax des Listenverständnisses als auch die Do-Notation sind syntaktischer Zucker für Verkettungsoperationen mit dem>>=Operator. Eine Monade ist im Grunde nur ein Typ, der das unterstützt>>=Operator.

Beispiel 3: Ein Parser

Dies ist ein sehr einfacher Parser, der entweder eine Zeichenfolge in Anführungszeichen oder eine Zahl analysiert:

parseExpr = parseString <|> parseNumber

parseString = do
        char '"'
        x <- many (noneOf "\"")
        char '"'
        return (StringValue x)

parseNumber = do
    num <- many1 digit
    return (NumberValue (read num))

Die Operationenchar, digitusw. sind ziemlich einfach. Sie stimmen entweder überein oder stimmen nicht überein. Die Magie ist die Monade, die den Kontrollfluss verwaltet: Die Operationen werden nacheinander ausgeführt, bis ein Match fehlschlägt. In diesem Fall zieht sich die Monade auf den neuesten Stand zurück<|>und versucht die nächste Option. Wieder eine Möglichkeit, Operationen mit einer zusätzlichen, nützlichen Semantik zu verketten.

Beispiel 4: Asynchrone Programmierung

Die obigen Beispiele sind in Haskell, aber es stellt sich herausF#unterstützt auch Monaden. Dieses Beispiel wird gestohlen vonDon Syme:

let AsyncHttp(url:string) =
    async {  let req = WebRequest.Create(url)
             let! rsp = req.GetResponseAsync()
             use stream = rsp.GetResponseStream()
             use reader = new System.IO.StreamReader(stream)
             return reader.ReadToEnd() }

Diese Methode ruft eine Webseite ab. Die Pointe ist die Verwendung vonGetResponseAsync- Es wartet tatsächlich auf die Antwort in einem separaten Thread, während der Haupt-Thread von der Funktion zurückkehrt. Die letzten drei Zeilen werden auf dem erzeugten Thread ausgeführt, wenn die Antwort empfangen wurde.

In den meisten anderen Sprachen müssten Sie explizit eine separate Funktion für die Zeilen erstellen, die die Antwort verarbeiten. DasasyncMonade ist in der Lage, den Block selbst zu "teilen" und die Ausführung der zweiten Hälfte zu verschieben. (Dasasync {}Die Syntax gibt an, dass der Kontrollfluss im Block durch das definiert istasyncMonade.)

Wie sie arbeiten

Wie kann eine Monade all diese ausgefallenen Kontrollfluss-Dinge tun? Was passiert eigentlich in einem Do-Block (oder einemBerechnungsausdruckwie sie in F # genannt werden, ist, dass jede Operation (im Grunde jede Zeile) in eine separate anonyme Funktion eingeschlossen ist. Diese Funktionen werden dann mit dem kombiniertbindOperator (buchstabiert>>=in Haskell). Seit derbindDie Operation kombiniert Funktionen und kann sie nach Belieben ausführen: nacheinander, mehrmals, in umgekehrter Reihenfolge, einige verwerfen, einige in einem separaten Thread ausführen, wenn es Ihnen gefällt, und so weiter.

Dies ist beispielsweise die erweiterte Version des E / A-Codes aus Beispiel 2:

putStrLn "What is your name?"
>>= (\_ -> getLine)
>>= (\name -> putStrLn ("Welcome, " ++ name ++ "!"))

Das ist hässlicher, aber es ist auch offensichtlicher, was tatsächlich vor sich geht. Das>>=Der Operator ist die magische Zutat: Er nimmt einen Wert (auf der linken Seite) und kombiniert ihn mit einer Funktion (auf der rechten Seite), um einen neuen Wert zu erzeugen. Dieser neue Wert wird dann vom nächsten übernommen>>=Operator und erneut mit einer Funktion kombiniert, um einen neuen Wert zu erzeugen.>>=kann als Mini-Evaluator angesehen werden.

Beachten Sie, dass>>=ist für verschiedene Typen überladen, so dass jede Monade ihre eigene Implementierung von hat>>=. (Alle Operationen in der Kette müssen jedoch vom Typ derselben Monade sein, andernfalls muss die>>=Operator funktioniert nicht.)

Die einfachste mögliche Implementierung von>>=Nimmt einfach den Wert auf der linken Seite und wendet ihn auf die Funktion auf der rechten Seite an und gibt das Ergebnis zurück. Wie bereits erwähnt, ist das gesamte Muster jedoch nützlich, wenn in der Implementierung der Monade etwas Besonderes vor sich geht>>=.

Es gibt einige zusätzliche Klugheit bei der Übergabe der Werte von einer Operation zur nächsten, dies erfordert jedoch eine eingehendere Erläuterung des Haskell-Typsystems.

Zusammenfassen

In Haskell-Begriffen ist eine Monade ein parametrisierter Typ, der eine Instanz der Monadentypklasse ist, die definiert>>=zusammen mit einigen anderen Betreibern. Für Laien ist eine Monade nur ein Typ, für den die>>=Operation ist definiert.

An sich>>=ist nur eine umständliche Art, Funktionen zu verketten, aber mit der Do-Notation, die das "Sanitär" verbirgt, erweisen sich die monadischen Operationen als eine sehr schöne und nützliche Abstraktion, die an vielen Stellen in der Sprache nützlich und zum Erstellen nützlich ist Ihre eigenen Mini-Sprachen in der Sprache.

Warum sind Monaden schwer?

Für viele Haskell-Lernende sind Monaden ein Hindernis, das sie wie eine Mauer treffen. Es ist nicht so, dass Monaden selbst komplex sind, sondern dass die Implementierung auf vielen anderen erweiterten Haskell-Funktionen wie parametrisierten Typen, Typklassen usw. beruht. Das Problem ist, dass Haskell I / O auf Monaden basiert und I / O wahrscheinlich eines der ersten Dinge ist, die Sie beim Erlernen einer neuen Sprache verstehen möchten - schließlich macht es nicht viel Spaß, Programme zu erstellen, die keine produzieren Ausgabe. Ich habe keine unmittelbare Lösung für dieses Henne-Ei-Problem, außer die Behandlung von E / A wie "Magie passiert hier", bis Sie genug Erfahrung mit anderen Teilen der Sprache haben. Es tut uns leid.

Ausgezeichneter Blog über Monaden:http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

Quelle
Translate

"Was ist eine Monade?" Zu erklären ist ein bisschen wie "Was ist eine Zahl?" Wir verwenden ständig Zahlen. Aber stellen Sie sich vor, Sie haben jemanden getroffen, der nichts über Zahlen wusste. Wie zumTeufelWürden Sie erklären, was Zahlen sind? Und wie würden Sie überhaupt beschreiben, warum das nützlich sein könnte?

Was ist eine Monade? Die kurze Antwort: Es ist eine spezielle Art, Operationen miteinander zu verketten.

Im Wesentlichen schreiben Sie Ausführungsschritte und verknüpfen sie mit der "Bindefunktion". (In Haskell heißt es>>=.) Sie können die Aufrufe selbst an den Bind-Operator schreiben oder Syntaxzucker verwenden, wodurch der Compiler diese Funktionsaufrufe für Sie einfügt. In beiden Fällen wird jeder Schritt durch einen Aufruf dieser Bindefunktion getrennt.

Die Bindungsfunktion ist also wie ein Semikolon. Es trennt die Schritte in einem Prozess. Die Aufgabe der Bindefunktion besteht darin, die Ausgabe aus dem vorherigen Schritt zu übernehmen und in den nächsten Schritt einzuspeisen.

Das klingt nicht zu schwer, oder? Aber da istmehr als eineArt von Monade. Warum? Wie?

Nun, die BindefunktionkönnenNehmen Sie einfach das Ergebnis aus einem Schritt und führen Sie es in den nächsten Schritt ein. Aber wenn das "alles" ist, was die Monade tut ... ist das eigentlich nicht sehr nützlich. Und das ist wichtig zu verstehen: JedernützlichMonade macht etwas anderesund dazunur eine Monade zu sein. JedernützlichMonade hat eine "besondere Kraft", die es einzigartig macht.

(Eine Monade, die das tutnichtsSpecial heißt "Identitätsmonade". Ähnlich wie die Identitätsfunktion klingt dies wie eine völlig sinnlose Sache, stellt sich jedoch als nicht ... Aber das ist eine andere Geschichte ™.)

Grundsätzlich hat jede Monade ihre eigene Implementierung der Bindefunktion. Und Sie können eine Bindefunktion so schreiben, dass sie zwischen den Ausführungsschritten Fehler macht. Zum Beispiel:

  • Wenn jeder Schritt eine Erfolgs- / Fehleranzeige zurückgibt, können Sie den nächsten Schritt nur dann binden lassen, wenn der vorherige erfolgreich war. Auf diese Weise bricht ein fehlgeschlagener Schritt die gesamte Sequenz "automatisch" ab, ohne dass Sie einen bedingten Test durchführen müssen. (DasFehler Monade.)

  • Wenn Sie diese Idee erweitern, können Sie "Ausnahmen" implementieren. (DasFehler MonadeoderAusnahme Monade.) Da Sie sie selbst definieren und keine Sprachfunktion haben, können Sie definieren, wie sie funktionieren. (ZB möchten Sie vielleicht die ersten beiden Ausnahmen ignorieren und nur abbrechen, wenn adritteAusnahme wird ausgelöst.)

  • Sie können jeden Schritt zurückgebenmehrere Ergebnisseund lassen Sie die Bindungsfunktionsschleife über sich ziehen und führen Sie jeden einzelnen in den nächsten Schritt für Sie ein. Auf diese Weise müssen Sie nicht ständig Schleifen schreiben, wenn Sie mit mehreren Ergebnissen arbeiten. Die Bindefunktion "automatisch" erledigt das alles für Sie. (DasListe Monade.)

  • Sie können nicht nur ein "Ergebnis" von einem Schritt zum anderen übergeben, sondern auch die Bindefunktion verwendenzusätzliche Daten übergebenauch herum. Diese Daten werden jetzt nicht in Ihrem Quellcode angezeigt, aber Sie können trotzdem von überall darauf zugreifen, ohne sie manuell an jede Funktion übergeben zu müssen. (DasLesermonade.)

  • Sie können es so gestalten, dass die "zusätzlichen Daten" ersetzt werden können. Dies ermöglicht es Ihnendestruktive Updates simulieren, ohne tatsächlich destruktive Updates durchzuführen. (DasStaatsmonadeund sein Cousin derSchriftsteller Monade.)

  • Weil du nur bistsimulierenMit destruktiven Updates können Sie trivial Dinge tun, mit denen dies unmöglich wäreechtdestruktive Updates. Zum Beispiel können SieMachen Sie das letzte Update rückgängig, oderauf eine ältere Version zurückgreifen.

  • Sie können eine Monade erstellen, in der Berechnungen möglich sindhielt inneSie können Ihr Programm anhalten, interne Statusdaten basteln und dann fortsetzen.

  • Sie können "Fortsetzungen" als Monade implementieren. Dies ermöglicht es IhnenBrich die Gedanken der Leute!

All dies und mehr ist mit Monaden möglich. All dies ist natürlich auch durchaus möglichohneMonaden auch. Es ist nur drastischeinfachermit Monaden.

Quelle
Translate

Entgegen dem allgemeinen Verständnis von Monaden haben sie eigentlich nichts mit Staat zu tun. Monaden sind einfach eine Möglichkeit, Dinge zu verpacken und Methoden bereitzustellen, um Operationen an den verpackten Sachen durchzuführen, ohne sie auszupacken.

Sie können beispielsweise in Haskell einen Typ erstellen, um einen anderen zu verpacken:

data Wrapped a = Wrap a

Um Dinge zu verpacken, die wir definieren

return :: a -> Wrapped a
return x = Wrap x

Angenommen, Sie haben eine Funktion, um Vorgänge ohne Auspacken auszuführenf :: a -> b, dann können Sie dies tunAufzugdiese Funktion, um auf umschlossene Werte einzuwirken:

fmap :: (a -> b) -> (Wrapped a -> Wrapped b)
fmap f (Wrap x) = Wrap (f x)

Das ist ungefähr alles, was es zu verstehen gibt. Es stellt sich jedoch heraus, dass es eine allgemeinere Funktion gibt, um dies zu tunHeben, welches istbind:

bind :: (a -> Wrapped b) -> (Wrapped a -> Wrapped b)
bind f (Wrap x) = f x

bindkann ein bisschen mehr alsfmap, aber nicht umgekehrt. Tatsächlich,fmapkann nur in Bezug auf definiert werdenbindundreturn. Wenn Sie also eine Monade definieren, geben Sie ihren Typ an (hier war esWrapped a) und dann sag wie es istreturnundbindOperationen arbeiten.

Das Coole ist, dass sich herausstellt, dass dies ein so allgemeines Muster ist, dass es überall auftaucht. Die reine Verkapselung des Zustands ist nur eines davon.

Einen guten Artikel darüber, wie Monaden verwendet werden können, um funktionale Abhängigkeiten einzuführen und damit die Reihenfolge der Auswertung zu steuern, wie sie in Haskells IO-Monade verwendet wird, finden Sie unterIO Inside.

Machen Sie sich keine Sorgen, wenn Sie Monaden verstehen wollen. Lesen Sie über sie, was Sie interessant finden, und machen Sie sich keine Sorgen, wenn Sie nicht sofort verstehen. Dann ist es der richtige Weg, nur in einer Sprache wie Haskell zu tauchen. Monaden sind eines dieser Dinge, bei denen das Verstehen durch Übung in Ihr Gehirn eindringt. Eines Tages merkt man plötzlich, dass man sie versteht.

Quelle
Translate

Aber,Du hättest Monaden erfinden können!

Sigfpe sagt:

Aber all dies führt Monaden als etwas Esoterisches ein, das einer Erklärung bedarf. Aber ich möchte argumentieren, dass sie überhaupt nicht esoterisch sind. Angesichts verschiedener Probleme bei der funktionalen Programmierung wären Sie unaufhaltsam zu bestimmten Lösungen geführt worden, die alle Beispiele für Monaden sind. Tatsächlich hoffe ich, Sie dazu zu bringen, sie jetzt zu erfinden, wenn Sie es noch nicht getan haben. Es ist dann ein kleiner Schritt zu bemerken, dass alle diese Lösungen tatsächlich dieselbe getarnte Lösung sind. Und nachdem Sie dies gelesen haben, sind Sie möglicherweise besser in der Lage, andere Dokumente zu Monaden zu verstehen, da Sie alles, was Sie sehen, als etwas erkennen, das Sie bereits erfunden haben.

Viele der Probleme, die Monaden zu lösen versuchen, hängen mit dem Problem der Nebenwirkungen zusammen. Also fangen wir mit ihnen an. (Beachten Sie, dass Sie mit Monaden mehr als nur mit Nebenwirkungen umgehen können. Insbesondere viele Arten von Containerobjekten können als Monaden angesehen werden. Bei einigen Einführungen in Monaden ist es schwierig, diese beiden unterschiedlichen Verwendungen von Monaden miteinander in Einklang zu bringen und sich auf nur eine oder mehrere zu konzentrieren das andere.)

In einer imperativen Programmiersprache wie C ++ verhalten sich Funktionen nicht wie die Funktionen der Mathematik. Angenommen, wir haben eine C ++ - Funktion, die ein einzelnes Gleitkommaargument verwendet und ein Gleitkommaergebnis zurückgibt. Oberflächlich betrachtet mag es ein wenig wie eine mathematische Funktion erscheinen, die Reals Reals zuordnet, aber eine C ++ - Funktion kann mehr als nur eine Zahl zurückgeben, die von ihren Argumenten abhängt. Es kann die Werte globaler Variablen lesen und schreiben sowie Ausgaben auf den Bildschirm schreiben und Eingaben vom Benutzer empfangen. In einer reinen funktionalen Sprache kann eine Funktion jedoch nur lesen, was ihr in ihren Argumenten geliefert wird, und die einzige Möglichkeit, wie sie sich auf die Welt auswirken kann, sind die zurückgegebenen Werte.

Quelle
Translate

Eine Monade ist ein Datentyp mit zwei Operationen:>>=(akabind) undreturn(akaunit).returnnimmt einen beliebigen Wert und erstellt damit eine Instanz der Monade.>>=Nimmt eine Instanz der Monade und ordnet eine Funktion darüber zu. (Sie können bereits sehen, dass eine Monade ein seltsamer Datentyp ist, da Sie in den meisten Programmiersprachen keine Funktion schreiben konnten, die einen beliebigen Wert annimmt und daraus einen Typ erstellt. Monaden verwenden eine Artparametrischer Polymorphismus.)

In der Haskell-Notation wird die Monadenschnittstelle geschrieben

class Monad m where
  return :: a -> m a
  (>>=) :: forall a b . m a -> (a -> m b) -> m b

Diese Operationen sollen bestimmten "Gesetzen" gehorchen, aber das ist nicht besonders wichtig: Die "Gesetze" kodifizieren nur das Verhalten vernünftiger Implementierungen der Operationen (im Grunde genommen das)>>=undreturnsollte sich darüber einig sein, wie Werte in Monadeninstanzen umgewandelt werden und das>>=ist assoziativ).

Bei Monaden geht es nicht nur um Zustand und E / A: Sie abstrahieren ein gemeinsames Berechnungsmuster, das die Arbeit mit Zustand, E / A, Ausnahmen und Nichtdeterminismus umfasst. Die wahrscheinlich am einfachsten zu verstehenden Monaden sind Listen und Optionstypen:

instance Monad [ ] where
    []     >>= k = []
    (x:xs) >>= k = k x ++ (xs >>= k)
    return x     = [x]

instance Monad Maybe where
    Just x  >>= k = k x
    Nothing >>= k = Nothing
    return x      = Just x

wo[]und:sind die Listenkonstruktoren,++ist der Verkettungsoperator undJustundNothingsind dieMaybeKonstruktoren. Beide Monaden kapseln gemeinsame und nützliche Berechnungsmuster für ihre jeweiligen Datentypen (beachten Sie, dass beides nichts mit Nebenwirkungen oder E / A zu tun hat).

Sie müssen wirklich herumspielen, um einen nicht trivialen Haskell-Code zu schreiben, um zu verstehen, worum es bei Monaden geht und warum sie nützlich sind.

Quelle
Translate

Sie sollten zuerst verstehen, was ein Funktor ist. Verstehe vorher Funktionen höherer Ordnung.

A Funktion höherer Ordnungist einfach eine Funktion, die eine Funktion als Argument verwendet.

A Funktorist jede Art von KonstruktionTNennen Sie eine Funktion höherer Ordnung, für die es eine Funktion höherer Ordnung gibtmap, das transformiert eine Funktion vom Typa -> b(gegeben zwei beliebige Typenaundb) in eine FunktionT a -> T b. DiesemapDie Funktion muss auch den Gesetzen der Identität und Zusammensetzung entsprechen, so dass die folgenden Ausdrücke für alle wahr sindpundq(Haskell-Notation):

map id = id
map (p . q) = map p . map q

Zum Beispiel ein Typkonstruktor namensListist ein Funktor, wenn er mit einer Funktion vom Typ ausgestattet ist(a -> b) -> List a -> List bdie den oben genannten Gesetzen gehorcht. Die einzige praktische Umsetzung liegt auf der Hand. Das ErgebnisList a -> List bFunktion iteriert über die angegebene Liste und ruft die(a -> b)Funktion für jedes Element und gibt die Liste der Ergebnisse zurück.

A Monadeist im Wesentlichen nur ein FunktorTmit zwei zusätzlichen Methoden,joinvom TypT (T a) -> T a, undunit(manchmal genanntreturn, fork, oderpure) vom Typa -> T a. Für Listen in Haskell:

join :: [[a]] -> [a]
pure :: a -> [a]

Warum ist das nützlich? Weil Sie zum Beispiel könntenmapüber eine Liste mit einer Funktion, die eine Liste zurückgibt.JoinNimmt die resultierende Liste der Listen und verkettet sie.Listist eine Monade, weil dies möglich ist.

Sie können eine Funktion schreiben, die dies tutmap, dannjoin. Diese Funktion wird aufgerufenbind, oderflatMap, oder(>>=), oder(=<<). So wird normalerweise eine Monadeninstanz in Haskell angegeben.

Eine Monade muss bestimmte Gesetze erfüllen, nämlich dasjoinmuss assoziativ sein. Dies bedeutet, wenn Sie einen Wert habenxvom Typ[[[a]]]dannjoin (join x)sollte gleich seinjoin (map join x). Undpuremuss eine Identität sein fürjoinso dassjoin (pure x) == x.

Quelle
Translate

[Haftungsausschluss: Ich versuche immer noch, Monaden vollständig zu groken. Das Folgende ist genau das, was ich bisher verstanden habe. Wenn es falsch ist, ruft mich hoffentlich jemand mit Fachkenntnissen auf dem Teppich an.]

Arnar schrieb:

Monaden sind einfach eine Möglichkeit, Dinge zu verpacken und Methoden bereitzustellen, um Operationen an den verpackten Sachen durchzuführen, ohne sie auszupacken.

Genau das ist es. Die Idee geht so:

  1. Sie nehmen einen Wert und verpacken ihn mit zusätzlichen Informationen. So wie der Wert von einer bestimmten Art ist (z. B. eine Ganzzahl oder eine Zeichenfolge), so sind die zusätzlichen Informationen von einer bestimmten Art.

    ZB könnten diese zusätzlichen Informationen a seinMaybeoder einIO.

  2. Dann haben Sie einige Operatoren, mit denen Sie die umschlossenen Daten bearbeiten können, während Sie diese zusätzlichen Informationen mitnehmen. Diese Operatoren verwenden die zusätzlichen Informationen, um zu entscheiden, wie das Verhalten der Operation für den umschlossenen Wert geändert werden soll.

    ZB aMaybe Intkann ein seinJust IntoderNothing. Nun, wenn Sie eine hinzufügenMaybe Intzu einemMaybe IntDer Bediener prüft, ob beide vorhanden sindJust Ints drinnen, und wenn ja, wird das auspackenInts, übergeben Sie ihnen den Additionsoperator und verpacken Sie das Ergebnis erneutIntin eine neueJust Int(das ist eine gültigeMaybe Int) und geben damit a zurückMaybe Int. Aber wenn einer von ihnen ein warNothingIm Inneren kehrt dieser Bediener sofort zurückNothing, was wiederum eine gültige istMaybe Int. Auf diese Weise können Sie so tun, als ob SieMaybe Ints sind nur normale Zahlen und führen regelmäßige Berechnungen durch. Wenn Sie eine bekommen würdenNothingIhre Gleichungen liefern immer noch das richtige Ergebnis -ohne dass Sie nach Wurfschecks suchen müssenNothingüberall.

Aber das Beispiel ist genau das, wofür es passiertMaybe. Wenn die zusätzlichen Informationen ein warenIO, dann der spezielle Operator definiert fürIOs würde stattdessen aufgerufen werden, und es könnte etwas völlig anderes tun, bevor die Addition durchgeführt wird. (OK, zwei hinzufügenIO Ints zusammen ist wahrscheinlich unsinnig - ich bin mir noch nicht sicher.) (Auch wenn Sie auf das geachtet habenMaybeSie haben beispielsweise festgestellt, dass das Umschließen eines Werts mit zusätzlichen Inhalten nicht immer korrekt ist. Aber es ist schwer, genau, korrekt und präzise zu sein, ohne unergründlich zu sein.)

Grundsätzlich,"Monade" bedeutet ungefähr "Muster". Aber anstatt eines Buches voller informell erklärter und speziell benannter Muster haben Sie jetztein Sprachkonstrukt- Syntax und alles - das erlaubt IhnenDeklarieren Sie neue Muster als Dinge in Ihrem Programm. (Die Ungenauigkeit hier ist, dass alle Muster einer bestimmten Form folgen müssen, daher ist eine Monade nicht ganz so allgemein wie ein Muster. Aber ich denke, das ist der engste Begriff, den die meisten Menschen kennen und verstehen.)

Und deshalb finden die Leute Monaden so verwirrend: weil sie so ein generisches Konzept sind. Zu fragen, was etwas zu einer Monade macht, ist ähnlich vage wie zu fragen, was etwas zu einem Muster macht.

Aber denken Sie an die Implikationen einer syntaktischen Unterstützung in der Sprache für die Idee eines Musters: anstatt das lesen zu müssenGruppe von vierBuchen und merken Sie sich die Konstruktion eines bestimmten MustersSchreiben Sie Code, der dieses Muster auf agnostische, generische Weise implementierteinmal und dann bist du fertig! Sie können dieses Muster dann wiederverwenden, z. B. Besucher oder Strategie oder Fassade oder was auch immer, indem Sie die Vorgänge in Ihrem Code damit dekorieren, ohne es immer wieder neu implementieren zu müssen!

Deshalb Menschen, dieverstehenMonaden finden sie sonützlich: Es ist kein Elfenbeinturm-Konzept, auf dessen Verständnis intellektuelle Snobs stolz sind (OK, das natürlich auch, Teehee), aber es macht den Code tatsächlich einfacher.

Quelle
Translate

Nach langem Bemühen denke ich, dass ich die Monade endlich verstehe. Nachdem ich meine lange Kritik an der überwiegend am besten bewerteten Antwort noch einmal gelesen habe, werde ich diese Erklärung anbieten.

Es gibt drei Fragen, die beantwortet werden müssen, um Monaden zu verstehen:

  1. Warum brauchst du eine Monade?
  2. Was ist eine Monade?
  3. Wie wird eine Monade implementiert?

Wie ich in meinen ursprünglichen Kommentaren festgestellt habe, sind zu viele Monadenerklärungen in Frage 3 enthalten, ohne und bevor Frage 2 oder Frage 1 wirklich angemessen behandelt werden.

Warum brauchst du eine Monade?

Reine funktionale Sprachen wie Haskell unterscheiden sich von imperativen Sprachen wie C oder Java darin, dass ein reines funktionales Programm nicht unbedingt Schritt für Schritt in einer bestimmten Reihenfolge ausgeführt wird. Ein Haskell-Programm ähnelt eher einer mathematischen Funktion, in der Sie die "Gleichung" in beliebig vielen möglichen Reihenfolgen lösen können. Dies bringt eine Reihe von Vorteilen mit sich, unter anderem, dass bestimmte Arten von Fehlern ausgeschlossen werden, insbesondere solche, die sich auf Dinge wie "Zustand" beziehen.

Es gibt jedoch bestimmte Probleme, die mit diesem Programmierstil nicht so einfach zu lösen sind. Einige Dinge, wie die Konsolenprogrammierung und die Datei-E / A, müssen in einer bestimmten Reihenfolge ausgeführt werden oder den Status beibehalten. Eine Möglichkeit, dieses Problem zu lösen, besteht darin, eine Art Objekt zu erstellen, das den Status einer Berechnung darstellt, sowie eine Reihe von Funktionen, die ein Statusobjekt als Eingabe verwenden und ein neues modifiziertes Statusobjekt zurückgeben.

Erstellen wir also einen hypothetischen "Status" -Wert, der den Status eines Konsolenbildschirms darstellt. Es ist nicht wichtig, wie genau dieser Wert aufgebaut ist. Angenommen, es handelt sich um ein Array von ASCII-Zeichen mit Bytelänge, das das darstellt, was derzeit auf dem Bildschirm angezeigt wird, und ein Array, das die letzte vom Benutzer eingegebene Eingabezeile im Pseudocode darstellt. Wir haben einige Funktionen definiert, die den Konsolenstatus annehmen, ändern und einen neuen Konsolenstatus zurückgeben.

consolestate MyConsole = new consolestate;

Um die Konsolenprogrammierung durchzuführen, aber auf rein funktionale Weise, müssten Sie viele Funktionsaufrufe ineinander verschachteln.

consolestate FinalConsole = print(input(print(myconsole, "Hello, what's your name?")),"hello, %inputbuffer%!");

Wenn Sie auf diese Weise programmieren, bleibt der "reine" Funktionsstil erhalten, während Änderungen an der Konsole in einer bestimmten Reihenfolge vorgenommen werden. Aber wir werden wahrscheinlich mehr als nur ein paar Operationen gleichzeitig ausführen wollen, wie im obigen Beispiel. Verschachtelungsfunktionen auf diese Weise werden unansehnlich. Was wir wollen, ist Code, der im Wesentlichen dasselbe wie oben tut, aber ein bisschen mehr so geschrieben ist:

consolestate FinalConsole = myconsole:
                            print("Hello, what's your name?"):
                            input():
                            print("hello, %inputbuffer%!");

Dies wäre in der Tat eine bequemere Art, es zu schreiben. Wie machen wir das aber?

Was ist eine Monade?

Sobald Sie einen Typ haben (wie zconsolestate), die Sie zusammen mit einer Reihe von Funktionen definieren, die speziell für diesen Typ entwickelt wurden, können Sie das gesamte Paket dieser Dinge in eine "Monade" verwandeln, indem Sie einen Operator wie definieren:(bind), das automatisch die Rückgabewerte links in die Funktionsparameter rechts einspeist und aliftOperator, der normale Funktionen in Funktionen umwandelt, die mit dieser speziellen Art von Bindungsoperator arbeiten.

Wie wird eine Monade implementiert?

Sehen Sie andere Antworten, die ziemlich frei zu sein scheinen, um ins Detail zu springen.

Quelle
Translate

(Siehe auch die Antworten unterWas ist eine Monade?)

Eine gute Motivation für Monaden ist die von Sigfpe (Dan Piponi)Du hättest Monaden erfinden können! (Und vielleicht haben Sie schon). Es gibtviele andere Monaden-TutorialsViele von ihnen versuchen fälschlicherweise, Monaden mit verschiedenen Analogien in "einfachen Worten" zu erklären: Dies ist dieMonad Tutorial Irrtum;; vermeide sie.

Wie DR MacIver in sagtSagen Sie uns, warum Ihre Sprache scheiße ist:

Also, Dinge, die ich an Haskell hasse:

Beginnen wir mit dem Offensichtlichen. Monaden-Tutorials. Nein, keine Monaden. Speziell die Tutorials. Sie sind endlos, übertrieben und lieber Gott, sie sind langweilig. Außerdem habe ich nie überzeugende Beweise dafür gesehen, dass sie tatsächlich helfen. Lesen Sie die Klassendefinition, schreiben Sie Code und überwinden Sie den beängstigenden Namen.

Sie sagen, Sie verstehen die Vielleicht-Monade? Gut, du bist auf dem Weg. Fangen Sie einfach an, andere Monaden zu verwenden, und früher oder später werden Sie verstehen, was Monaden im Allgemeinen sind.

[Wenn Sie mathematisch orientiert sind, möchten Sie möglicherweise Dutzende von Tutorials ignorieren und die Definition lernen oder folgenVorlesungen in Kategorietheorie:) Der Hauptteil der Definition besteht darin, dass eine Monade M einen "Typkonstruktor" enthält, der für jeden vorhandenen Typ "T" einen neuen Typ "MT" definiert, und einige Möglichkeiten zum Hin- und Herwechseln zwischen "regulären" Typen und " M "-Typen.]

Überraschenderweise ist eine der besten Einführungen in Monaden tatsächlich eine der frühen wissenschaftlichen Arbeiten, in denen Monaden vorgestellt werden, Philip WadlersMonaden zur funktionalen Programmierung. Es hat tatsächlich praktische,nicht trivialmotivierende Beispiele, im Gegensatz zu vielen anderen künstlichen Tutorials.

Quelle
Translate

Eine Monade ist effektiv eine Form des "Typoperators". Es wird drei Dinge tun. Zuerst wird ein Wert eines Typs in einen anderen Typ "umbrochen" (oder auf andere Weise konvertiert) (normalerweise als "monadischer Typ" bezeichnet). Zweitens werden alle Operationen (oder Funktionen) des zugrunde liegenden Typs für den monadischen Typ verfügbar gemacht. Schließlich wird es Unterstützung für die Kombination seines Selbst mit einer anderen Monade bieten, um eine zusammengesetzte Monade zu erzeugen.

Die "Vielleicht-Monade" entspricht im Wesentlichen den "nullbaren Typen" in Visual Basic / C #. Es nimmt einen nicht nullbaren Typ "T" und konvertiert ihn in einen "nullbaren <T>" und definiert dann, was alle binären Operatoren auf einem nullbaren <T> bedeuten.

Nebenwirkungen werden ähnlich dargestellt. Es wird eine Struktur erstellt, die neben dem Rückgabewert einer Funktion auch Beschreibungen von Nebenwirkungen enthält. Die "aufgehobenen" Operationen kopieren dann Nebenwirkungen, wenn Werte zwischen Funktionen übergeben werden.

Sie werden aus mehreren Gründen eher als "Monaden" als als der leicht verständliche Name "Typoperatoren" bezeichnet:

  1. Monaden haben Einschränkungen, was sie tun können (siehe Definition für Details).
  2. Diese Einschränkungen entsprechen zusammen mit der Tatsache, dass es sich um drei Operationen handelt, der Struktur einer sogenannten Monade in der Kategorietheorie, die ein dunkler Zweig der Mathematik ist.
  3. Sie wurden von Befürwortern "reiner" funktionaler Sprachen entworfen
  4. Befürworter reiner funktionaler Sprachen wie obskure Zweige der Mathematik
  5. Da die Mathematik unklar ist und Monaden mit bestimmten Programmierstilen verbunden sind, neigen die Leute dazu, das Wort Monade als eine Art geheimen Händedruck zu verwenden. Aus diesem Grund hat sich niemand die Mühe gemacht, in einen besseren Namen zu investieren.
Quelle
Translate

Nachdem ich vor einigen Jahren eine Antwort auf diese Frage gegeben habe, glaube ich, dass ich diese Antwort verbessern und vereinfachen kann mit ...

Eine Monade ist eine Technik zur Funktionskomposition, die die Behandlung einiger Eingabeszenarien mithilfe einer Kompositionsfunktion externalisiert.bind, um Eingaben während der Komposition vorzuverarbeiten.

Bei normaler Zusammensetzung ist die Funktion,compose (>>)wird verwendet, um die zusammengesetzte Funktion nacheinander auf das Ergebnis ihres Vorgängers anzuwenden. Wichtig ist, dass die zusammengesetzte Funktion alle Szenarien ihrer Eingabe verarbeiten muss.

(x -> y) >> (y -> z)

Dieses Design kann verbessert werden, indem die Eingabe so umstrukturiert wird, dass relevante Zustände leichter abgefragt werden können. Also statt einfachyDer Wert kann werdenMbwie zum Beispiel(is_OK, b)wennyschloss einen Begriff der Gültigkeit ein.

Wenn die Eingabe beispielsweise möglicherweise nur eine Zahl ist, können Sie den Typ in eine Umstrukturierung umwandeln, anstatt eine Zeichenfolge zurückzugeben, die pflichtbewusst eine Zahl enthalten kann oder nichtboolAnzeigen des Vorhandenseins einer gültigen Nummer und einer Nummer im Tupel wie z.bool * float. Die zusammengesetzten Funktionen müssten nun keine Eingabezeichenfolge mehr analysieren, um festzustellen, ob eine Zahl vorhanden ist, sondern könnten lediglich die überprüfenboolTeil eines Tupels.

(Ma -> Mb) >> (Mb -> Mc)

Auch hier erfolgt die Zusammensetzung natürlich mitcomposeDaher muss jede Funktion alle Szenarien ihrer Eingabe einzeln behandeln, obwohl dies jetzt viel einfacher ist.

Was wäre jedoch, wenn wir den Aufwand für die Befragung in Zeiten auslagern könnten, in denen die Behandlung eines Szenarios Routine ist? Was ist zum Beispiel, wenn unser Programm nichts tut, wenn die Eingabe nicht in Ordnung ist, wie in wann?is_OKistfalse. Wenn dies getan würde, müssten zusammengesetzte Funktionen dieses Szenario nicht selbst behandeln, was ihren Code dramatisch vereinfacht und eine weitere Ebene der Wiederverwendung bewirkt.

Um diese Externalisierung zu erreichen, könnten wir eine Funktion verwenden,bind (>>=), um diecompositionAnstatt voncompose. Anstatt einfach Werte von der Ausgabe einer Funktion zur Eingabe einer anderen zu übertragenBindwürde das inspizierenMTeil vonMaund entscheiden, ob und wie die zusammengesetzte Funktion auf die angewendet werden solla. Natürlich die Funktionbindwürde speziell für unsere besondere definiert werdenMum in der Lage zu sein, seine Struktur zu inspizieren und jede Art von Anwendung durchzuführen, die wir wollen. Trotzdem ist dieakann da alles seinbindgeht nur dieanicht von der zusammengesetzten Funktion überprüft, wenn sie die erforderliche Anwendung bestimmt. Außerdem müssen sich die zusammengesetzten Funktionen selbst nicht mehr mit dem befassenMTeil der Eingabestruktur entweder, um sie zu vereinfachen. Daher...

(a -> Mb) >>= (b -> Mc)oder prägnanterMb >>= (b -> Mc)

Kurz gesagt, eine Monade externalisiert und liefert dadurch ein Standardverhalten bei der Behandlung bestimmter Eingabeszenarien, sobald die Eingabe so ausgelegt ist, dass sie ausreichend verfügbar ist. Dieser Entwurf ist einshell and contentModell, bei dem die Shell Daten enthält, die für die Anwendung der zusammengesetzten Funktion relevant sind und von der abgefragt werden und nur für die verfügbar bleibenbindFunktion.

Daher ist eine Monade drei Dinge:

  1. einMShell für monadenrelevante Informationen,
  2. a bindFunktion implementiert, um diese Shell-Informationen bei der Anwendung der zusammengesetzten Funktionen auf die in der Shell gefundenen Inhaltswerte zu verwenden, und
  3. zusammensetzbare Funktionen der Form,a -> MbDies führt zu Ergebnissen, die monadische Verwaltungsdaten enthalten.

Im Allgemeinen ist die Eingabe in eine Funktion weitaus restriktiver als ihre Ausgabe, die beispielsweise Fehlerbedingungen enthalten kann. daher dieMbDie Ergebnisstruktur ist im Allgemeinen sehr nützlich. Beispielsweise gibt der Divisionsoperator keine Zahl zurück, wenn der Divisor ist0.

Zusätzlich,monads kann Wrap-Funktionen enthalten, die Werte umbrechen.ain den monadischen Typ,Maund allgemeine Funktionen,a -> bin monadische Funktionen,a -> Mb, indem sie ihre Ergebnisse nach der Anwendung einpacken. Natürlich gernebindsind solche Wrap-Funktionen spezifisch fürM. Ein Beispiel:

let return a = [a]
let lift f a = return (f a)

Das Design derbindFunktion setzt unveränderliche Datenstrukturen und reine Funktionen voraus, andere Dinge werden komplex und Garantien können nicht gegeben werden. Als solche gibt es monadische Gesetze:

Gegeben...

M_ 
return = (a -> Ma)
f = (a -> Mb)
g = (b -> Mc)

Dann...

Left Identity  : (return a) >>= f === f a
Right Identity : Ma >>= return    === Ma
Associative    : Ma >>= (f >>= g) === Ma >>= ((fun x -> f x) >>= g)

Associativitybedeutet, dassbindbehält die Reihenfolge der Bewertung bei, unabhängig davon, wannbindwird angewandt. Das heißt, in der Definition vonAssociativityoben ist die Kraft frühzeitige Bewertung der Klammernbindingvonfundgführt nur zu einer erwarteten FunktionMaum die zu vervollständigenbind. Daher die Bewertung vonMamuss bestimmt werden, bevor sein Wert angewendet werden kannfund das Ergebnis wiederum angewendet aufg.

Quelle
Tim Lee
Translate

Monaden sollen den Fluss steuern, welche abstrakten Datentypen zu Daten gehören.

Mit anderen Worten, viele Entwickler sind mit der Idee von Sets, Listen, Wörterbüchern (oder Hashes oder Maps) und Bäumen vertraut. Innerhalb dieser Datentypen gibt es viele Sonderfälle (z. B. InsertionOrderPreservingIdentityHashMap).

Wenn viele Entwickler jedoch mit dem Programmfluss konfrontiert werden, sind sie nicht viel mehr Konstrukten ausgesetzt, als wenn, switch / case, while, goto (grr) und (vielleicht) Closures.

Eine Monade ist also einfach ein Kontrollflusskonstrukt. Ein besserer Ausdruck, um die Monade zu ersetzen, wäre "Kontrolltyp".

Daher verfügt eine Monade über Steckplätze für Steuerlogik, Anweisungen oder Funktionen. In Datenstrukturen bedeutet dies, dass Sie in einigen Datenstrukturen Daten hinzufügen und entfernen können.

Zum Beispiel die "wenn" -Monade:

if( clause ) then block

Im einfachsten Fall gibt es zwei Slots - eine Klausel und einen Block. DasifMonade wird normalerweise erstellt, um das Ergebnis der Klausel auszuwerten, und wenn nicht falsch, den Block auszuwerten. Viele Entwickler werden nicht in Monaden eingeführt, wenn sie "Wenn" lernen, und es ist einfach nicht notwendig, Monaden zu verstehen, um effektive Logik zu schreiben.

Monaden können komplizierter werden, genauso wie Datenstrukturen komplizierter werden können, aber es gibt viele breite Kategorien von Monaden, die eine ähnliche Semantik haben können, aber unterschiedliche Implementierungen und Syntax.

Natürlich können auf die gleiche Weise, wie Datenstrukturen über Monaden iteriert oder durchlaufen werden, Monaden ausgewertet werden.

Compiler unterstützen möglicherweise benutzerdefinierte Monaden oder nicht. Haskell sicherlich. Ioke hat einige ähnliche Fähigkeiten, obwohl der Begriff Monade in der Sprache nicht verwendet wird.

Quelle
Translate

Mein Lieblings-Monaden-Tutorial:

http://www.haskell.org/haskellwiki/All_About_Monads

(von 170.000 Treffern bei einer Google-Suche nach "Monad Tutorial"!)

@Stu: Der Sinn von Monaden besteht darin, dass Sie (normalerweise) sequentielle Semantik zu ansonsten reinem Code hinzufügen können. Sie können sogar Monaden erstellen (mithilfe von Monadentransformatoren) und eine interessantere und kompliziertere kombinierte Semantik erhalten, z. B. das Parsen mit Fehlerbehandlung, gemeinsam genutztem Status und Protokollierung. All dies ist in reinem Code möglich. Monaden ermöglichen es Ihnen lediglich, ihn zu abstrahieren und in modularen Bibliotheken wiederzuverwenden (immer gut in der Programmierung). Außerdem bieten Sie eine praktische Syntax, damit er zwingend erscheint.

Haskell hat bereits eine Überladung von Operatoren [1]: Es verwendet Typklassen ähnlich wie Schnittstellen in Java oder C #, aber Haskell lässt zufällig auch nicht-alphanumerische Token wie + && und> als Infix-IDs zu. Es ist nur eine Überladung des Operators in Ihrer Sichtweise, wenn Sie "Überladen des Semikolons" meinen [2]. Es klingt wie schwarze Magie und um Ärger zu bitten, "das Semikolon zu überladen" (Bild unternehmungslustige Perl-Hacker, die Wind von dieser Idee bekommen), aber der Punkt ist, dass ohne MonadenEs gibt kein Semikolon, da rein funktionaler Code keine explizite Sequenzierung erfordert oder zulässt.

Das klingt alles viel komplizierter als nötig. Der Artikel von sigfpe ist ziemlich cool, verwendet aber Haskell, um ihn zu erklären, was das Henne-Ei-Problem des Verstehens von Haskell zum Grok von Monaden und des Verstehens von Monaden zum Grok von Haskell nicht löst.

[1] Dies ist ein von Monaden getrenntes Problem, aber Monaden verwenden die Operatorüberladungsfunktion von Haskell.

[2] Dies ist auch eine übermäßige Vereinfachung, da der Operator für die Verkettung monadischer Aktionen >> = (ausgesprochen "bind") ist, aber es gibt syntaktischen Zucker ("do"), mit dem Sie geschweifte Klammern und Semikolons und / oder Einrückungen und Zeilenumbrüche verwenden können.

Quelle
Camille Lee
Translate

Ich habe in letzter Zeit anders über Monaden nachgedacht. Ich habe sie als abstrahierend angesehenAusführungsreihenfolgeauf mathematische Weise, die neue Arten von Polymorphismus ermöglicht.

Wenn Sie eine imperative Sprache verwenden und einige Ausdrücke der Reihe nach schreiben, wird der Code IMMER genau in dieser Reihenfolge ausgeführt.

Und im einfachen Fall, wenn Sie eine Monade verwenden, fühlt es sich genauso an - Sie definieren eine Liste von Ausdrücken, die in der richtigen Reihenfolge vorkommen. Je nachdem, welche Monade Sie verwenden, wird Ihr Code möglicherweise in der richtigen Reihenfolge (wie in der E / A-Monade) parallel über mehrere Elemente gleichzeitig (wie in der Listenmonade) ausgeführt und möglicherweise auf halbem Weg angehalten (wie in der Vielleicht-Monade). Es kann eine Pause einlegen, um später wieder aufgenommen zu werden (wie in einer Wiederaufnahme-Monade), es kann zurückgespult werden und von vorne beginnen (wie in einer Transaktions-Monade) oder es kann teilweise zurückgespult werden, um andere Optionen auszuprobieren (wie in einer Logik-Monade). .

Und da Monaden polymorph sind, können Sie je nach Bedarf denselben Code in verschiedenen Monaden ausführen.

In einigen Fällen ist es außerdem möglich, Monaden (mit Monadentransformatoren) miteinander zu kombinieren, um mehrere Funktionen gleichzeitig zu erhalten.

Quelle
Translate

Ich bin noch neu in Monaden, aber ich dachte, ich würde einen Link teilen, den ich gefunden habe und der sich wirklich gut zum Lesen anfühlte (MIT BILDERN !!):http://www.matusiak.eu/numerodix/blog/2012/3/11/monads-for-the-layman/(keine Zugehörigkeit)

Grundsätzlich war das warme und unscharfe Konzept, das ich aus dem Artikel erhalten habe, das Konzept, dass Monaden im Grunde genommen Adapter sind, mit denen unterschiedliche Funktionen auf komponierbare Weise funktionieren können, dh mehrere Funktionen aneinanderreihen und mischen und anpassen können, ohne sich um eine inkonsistente Rückkehr sorgen zu müssen Typen und so. Die BIND-Funktion ist also dafür verantwortlich, Äpfel mit Äpfeln und Orangen mit Orangen zu halten, wenn wir versuchen, diese Adapter herzustellen. Und die LIFT-Funktion ist dafür verantwortlich, "untergeordnete" Funktionen zu übernehmen und sie zu "aktualisieren", um mit BIND-Funktionen zu arbeiten und auch zusammensetzbar zu sein.

Ich hoffe, ich habe es richtig verstanden und vor allem hoffe ich, dass der Artikel eine gültige Sicht auf Monaden hat. Wenn nichts anderes, hat dieser Artikel meinen Appetit geweckt, mehr über Monaden zu lernen.

Quelle
Elliot Lee
Translate

Lassen Sie mich Ihnen zusätzlich zu den oben genannten hervorragenden Antworten einen Link zum folgenden Artikel (von Patrick Thomson) anbieten, in dem Monaden erläutert werden, indem das Konzept auf die JavaScript-Bibliothek bezogen wirdjQuery(und seine Art der Verwendung der "Methodenverkettung" zur Manipulation des DOM):jQuery ist eine Monade

DasjQuery-Dokumentationselbst bezieht sich nicht auf den Begriff "Monade", sondern spricht über das "Builder-Muster", das wahrscheinlich vertrauter ist. Dies ändert nichts an der Tatsache, dass Sie dort eine richtige Monade haben, ohne es zu merken.

Quelle
Esther Lee
Translate

Monaden sind keine Metaphern, aber eine praktisch nützliche Abstraktion, die aus einem gemeinsamen Muster hervorgeht, wie Daniel Spiewak erklärt.

Quelle
Duncan Lee
Translate

Eine Monade ist eine Möglichkeit, Berechnungen miteinander zu kombinieren, die einen gemeinsamen Kontext haben. Es ist wie ein Rohrnetz. Beim Aufbau des Netzwerks fließen keine Daten durch das Netzwerk. Aber wenn ich alle Bits mit 'bind' und 'return' zusammengesetzt habe, rufe ich so etwas aufrunMyMonad monad dataund die Daten fließen durch die Rohre.

Quelle
Boris Lee
Translate

In der Praxis ist monad eine benutzerdefinierte Implementierung eines Funktionskompositionsoperators, der Nebenwirkungen und inkompatible Eingabe- und Rückgabewerte (für die Verkettung) berücksichtigt.

Quelle
Max Lee
Translate

Wenn ich richtig verstanden habe, wird IEnumerable von Monaden abgeleitet. Ich frage mich, ob das ein interessanter Ansatz für diejenigen von uns aus der C # -Welt sein könnte.

Für das, was es wert ist, hier sind einige Links zu Tutorials, die mir geholfen haben (und nein, ich habe immer noch nicht verstanden, was Monaden sind).

Quelle
Clarence Lee
Translate

Die beiden Dinge, die mir am besten geholfen haben, als ich davon erfuhr, waren:

Kapitel 8, "Functional Parsers", aus Graham Huttons BuchProgrammierung in Haskell. Hier werden Monaden eigentlich überhaupt nicht erwähnt, aber wenn Sie Kapitel durcharbeiten und wirklich alles darin verstehen können, insbesondere wie eine Folge von Bindungsoperationen bewertet wird, werden Sie die Interna von Monaden verstehen. Erwarten Sie, dass dies mehrere Versuche dauert.

Das TutorialAlles über Monaden. Dies gibt einige gute Beispiele für ihre Verwendung, und ich muss sagen, dass die Analogie in Appendex ich für mich gearbeitet habe.

Quelle
Deborah Lee
Translate

Monoid scheint etwas zu sein, das sicherstellt, dass alle auf einem Monoid und einem unterstützten Typ definierten Operationen immer einen unterstützten Typ innerhalb des Monoid zurückgeben. ZB Beliebige Zahl + Beliebige Zahl = Eine Zahl, keine Fehler.

Während die Division zwei Brüche akzeptiert und einen Bruch zurückgibt, der die Division durch Null als Unendlichkeit in haskell irgendwo definiert (was zufällig ein Bruch ist) ...

In jedem Fall scheinen Monaden nur eine Möglichkeit zu sein, um sicherzustellen, dass sich Ihre Operationskette vorhersehbar verhält, und eine Funktion, die behauptet, Num -> Num zu sein, zusammengesetzt mit einer anderen Funktion von Num-> Num, die mit x aufgerufen wird, nicht Sagen Sie, schießen Sie die Raketen.

Wenn wir andererseits eine Funktion haben, die die Raketen abfeuert, können wir sie mit anderen Funktionen zusammensetzen, die auch die Raketen abfeuern, da unsere Absicht klar ist - wir wollen die Raketen abfeuern -, aber es wird nicht versucht Drucken von "Hello World" aus irgendeinem Grund.

In Haskell ist main vom Typ IO () oder IO [()], die Unterscheidung ist seltsam und ich werde nicht darauf eingehen, aber ich denke, dass Folgendes passiert:

Wenn ich main habe, möchte ich, dass es eine Kette von Aktionen ausführt. Der Grund, warum ich das Programm ausführe, ist, einen Effekt zu erzeugen - normalerweise über IO. Auf diese Weise kann ich E / A-Vorgänge hauptsächlich miteinander verketten, um E / A-Vorgänge auszuführen, sonst nichts.

Wenn ich versuche, etwas zu tun, das keine "E / A" zurückgibt, beschwert sich das Programm, dass die Kette nicht fließt, oder im Grunde genommen "Wie hängt dies mit dem zusammen, was wir versuchen - eine E / A-Aktion", scheint es zu erzwingen der Programmierer, um seinen Gedankengang beizubehalten, ohne abzuweichen und über das Abfeuern der Raketen nachzudenken, während er Algorithmen zum Sortieren erstellt - die nicht fließen.

Grundsätzlich scheinen Monaden ein Tipp für den Compiler zu sein: "Hey, Sie kennen diese Funktion, die hier eine Zahl zurückgibt. Sie funktioniert nicht immer, sie kann manchmal eine Zahl erzeugen und manchmal gar nichts. Behalten Sie dies einfach bei." Verstand". Wenn Sie dies wissen und versuchen, eine monadische Aktion zu behaupten, kann die monadische Aktion als Ausnahme für die Kompilierungszeit fungieren und sagen: "Hey, dies ist eigentlich keine Zahl, dies kann eine Zahl sein, aber Sie können nicht davon ausgehen, dass Sie etwas tun." um sicherzustellen, dass der Durchfluss akzeptabel ist. " Dies verhindert ein unvorhersehbares Programmverhalten - zu einem angemessenen Teil.

Es scheint, dass es bei Monaden nicht um Reinheit oder Kontrolle geht, sondern darum, die Identität einer Kategorie aufrechtzuerhalten, für die alles Verhalten vorhersehbar und definiert ist oder nicht kompiliert wird. Sie können nichts tun, wenn von Ihnen erwartet wird, dass Sie etwas tun, und Sie können nichts tun, wenn von Ihnen erwartet wird, dass Sie nichts tun (sichtbar).

Der größte Grund, an den ich für Monaden denken könnte, ist - schauen Sie sich den Prozedur- / OOP-Code an, und Sie werden feststellen, dass Sie nicht wissen, wo das Programm beginnt oder endet. Alles, was Sie sehen, ist viel Springen und viel Mathe , Magie und Raketen. Sie werden es nicht warten können, und wenn Sie können, werden Sie ziemlich viel Zeit damit verbringen, sich mit dem gesamten Programm zu beschäftigen, bevor Sie einen Teil davon verstehen können, da die Modularität in diesem Kontext auf voneinander abhängigen "Abschnitten" basiert. von Code, wobei der Code so optimiert ist, dass er so verwandt wie möglich ist, um Effizienz / Wechselbeziehung zu versprechen. Monaden sind sehr konkret und per Definition gut definiert und stellen sicher, dass der Programmfluss analysiert und Teile isoliert werden können, die schwer zu analysieren sind - da sie selbst Monaden sind. Eine Monade scheint eine "verständliche Einheit zu sein, die nach ihrem vollständigen Verständnis vorhersehbar ist". Wenn Sie die Monade "Vielleicht" verstehen, gibt es keine Möglichkeit, etwas anderes zu tun, als "Vielleicht", was trivial erscheint, aber in den meisten Fällen nicht monadisch Code, eine einfache Funktion "helloworld" kann die Raketen abfeuern, nichts tun oder das Universum zerstören oder sogar die Zeit verzerren - wir haben keine Ahnung und keine Garantie dafür, dass ES DAS IST, WAS ES IST. Eine Monade GARANTIERT, DASS ES IST, WAS ES IST. Das ist sehr mächtig.

Alle Dinge in der "realen Welt" scheinen Monaden zu sein, in dem Sinne, dass sie an bestimmte beobachtbare Gesetze gebunden sind, die Verwirrung verhindern. Dies bedeutet nicht, dass wir alle Operationen dieses Objekts nachahmen müssen, um Klassen zu erstellen. Stattdessen können wir einfach sagen: "Ein Quadrat ist ein Quadrat", nichts als ein Quadrat, nicht einmal ein Rechteck oder ein Kreis, und "ein Quadrat hat Fläche von der Länge einer der vorhandenen Dimensionen multipliziert mit sich selbst. Egal welches Quadrat Sie haben, wenn es sich um ein Quadrat im 2D-Raum handelt, kann seine Fläche absolut nichts anderes sein als seine quadratische Länge, es ist fast trivial zu beweisen. Dies ist sehr mächtig, weil Wir müssen keine Aussagen machen, um sicherzustellen, dass unsere Welt so ist, wie sie ist. Wir nutzen lediglich die Auswirkungen der Realität, um zu verhindern, dass unsere Programme aus der Bahn geraten.

Ich bin so ziemlich garantiert falsch, aber ich denke, das könnte jemandem da draußen helfen, also hoffentlich hilft es jemandem.

Quelle
Translate

Im Zusammenhang mit Scala finden Sie Folgendes als einfachste Definition. Grundsätzlich ist flatMap (oder bind) 'assoziativ' und es gibt eine Identität.

trait M[+A] {
  def flatMap[B](f: A => M[B]): M[B] // AKA bind

  // Pseudo Meta Code
  def isValidMonad: Boolean = {
    // for every parameter the following holds
    def isAssociativeOn[X, Y, Z](x: M[X], f: X => M[Y], g: Y => M[Z]): Boolean =
      x.flatMap(f).flatMap(g) == x.flatMap(f(_).flatMap(g))

    // for every parameter X and x, there exists an id
    // such that the following holds
    def isAnIdentity[X](x: M[X], id: X => M[X]): Boolean =
      x.flatMap(id) == x
  }
}

Z.B

// These could be any functions
val f: Int => Option[String] = number => if (number == 7) Some("hello") else None
val g: String => Option[Double] = string => Some(3.14)

// Observe these are identical. Since Option is a Monad 
// they will always be identical no matter what the functions are
scala> Some(7).flatMap(f).flatMap(g)
res211: Option[Double] = Some(3.14)

scala> Some(7).flatMap(f(_).flatMap(g))
res212: Option[Double] = Some(3.14)


// As Option is a Monad, there exists an identity:
val id: Int => Option[Int] = x => Some(x)

// Observe these are identical
scala> Some(7).flatMap(id)
res213: Option[Int] = Some(7)

scala> Some(7)
res214: Some[Int] = Some(7)

HINWEISGenau genommen ist die Definition von aMonade in der funktionalen Programmierungist nicht dasselbe wie die Definition von aMonade in der Kategorietheorie, die abwechselnd von definiert wirdmapundflatten. Obwohl sie unter bestimmten Zuordnungen eine Art Äquivalent sind. Diese Präsentation ist sehr gut:http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-category

Quelle
Translate

Diese Antwort beginnt mit einem motivierenden Beispiel, arbeitet das Beispiel durch, leitet ein Beispiel für eine Monade ab und definiert formal "Monade".

Betrachten Sie diese drei Funktionen im Pseudocode:

f(<x, messages>) := <x, messages "called f. ">
g(<x, messages>) := <x, messages "called g. ">
wrap(x)          := <x, "">

fnimmt ein bestelltes Paar des Formulars an<x, messages>und gibt ein bestelltes Paar zurück. Der erste Artikel bleibt unberührt und wird angehängt"called f. "zum zweiten Punkt. Das gleiche mitg.

Sie können diese Funktionen zusammenstellen und Ihren ursprünglichen Wert zusammen mit einer Zeichenfolge erhalten, die angibt, in welcher Reihenfolge die Funktionen aufgerufen wurden:

  f(g(wrap(x)))
= f(g(<x, "">))
= f(<x, "called g. ">)
= <x, "called g. called f. ">

Sie mögen die Tatsache nicht, dassfundgsind dafür verantwortlich, ihre eigenen Protokollnachrichten an die vorherigen Protokollierungsinformationen anzuhängen. (Stellen Sie sich aus Gründen der Argumentation vor, dass anstelle von Zeichenfolgen,fundgmuss eine komplizierte Logik für das zweite Element des Paares ausführen. Es wäre mühsam, diese komplizierte Logik in zwei oder mehr verschiedenen Funktionen zu wiederholen.)

Sie bevorzugen es, einfachere Funktionen zu schreiben:

f(x)    := <x, "called f. ">
g(x)    := <x, "called g. ">
wrap(x) := <x, "">

Aber schauen Sie sich an, was passiert, wenn Sie sie komponieren:

  f(g(wrap(x)))
= f(g(<x, "">))
= f(<<x, "">, "called g. ">)
= <<<x, "">, "called g. ">, "called f. ">

Das Problem ist, dassVorbeigehenEin Paar in eine Funktion gibt Ihnen nicht das, was Sie wollen. Aber was wäre, wenn du könntest?Futterein Paar in eine Funktion:

  feed(f, feed(g, wrap(x)))
= feed(f, feed(g, <x, "">))
= feed(f, <x, "called g. ">)
= <x, "called g. called f. ">

Lesenfeed(f, m)als "Feedminf". ZuFutterein Paar<x, messages>in eine Funktionfist zubestehen xinf, bekommen<y, message>ausf, und zurück<y, messages message>.

feed(f, <x, messages>) := let <y, message> = f(x)
                          in  <y, messages message>

Beachten Sie, was passiert, wenn Sie drei Dinge mit Ihren Funktionen tun:

Erstens: Wenn Sie einen Wert einschließen und dannFutterdas resultierende Paar in eine Funktion:

  feed(f, wrap(x))
= feed(f, <x, "">)
= let <y, message> = f(x)
  in  <y, "" message>
= let <y, message> = <x, "called f. ">
  in  <y, "" message>
= <x, "" "called f. ">
= <x, "called f. ">
= f(x)

Das ist das gleiche wieVorbeigehender Wert in die Funktion.

Zweitens: Wenn Sie ein Paar fütternwrap:

  feed(wrap, <x, messages>)
= let <y, message> = wrap(x)
  in  <y, messages message>
= let <y, message> = <x, "">
  in  <y, messages message>
= <x, messages "">
= <x, messages>

Das ändert nichts an dem Paar.

Drittens: Wenn Sie eine Funktion definieren, die übernimmtxund füttertg(x)inf:

h(x) := feed(f, g(x))

und füttere ein Paar hinein:

  feed(h, <x, messages>)
= let <y, message> = h(x)
  in  <y, messages message>
= let <y, message> = feed(f, g(x))
  in  <y, messages message>
= let <y, message> = feed(f, <x, "called g. ">)
  in  <y, messages message>
= let <y, message> = let <z, msg> = f(x)
                     in  <z, "called g. " msg>
  in <y, messages message>
= let <y, message> = let <z, msg> = <x, "called f. ">
                     in  <z, "called g. " msg>
  in <y, messages message>
= let <y, message> = <x, "called g. " "called f. ">
  in <y, messages message>
= <x, messages "called g. " "called f. ">
= feed(f, <x, messages "called g. ">)
= feed(f, feed(g, <x, messages>))

Das ist das gleiche wie das Einspeisen des Paaresgund Einspeisen des resultierenden Paares inf.

Sie haben fast eine Monade. Jetzt müssen Sie nur noch die Datentypen in Ihrem Programm kennen.

Welche Art von Wert ist<x, "called f. ">? Nun, das hängt davon ab, welche Art von Wertxist. Wennxist vom Typt, dann ist Ihr Paar ein Wert vom Typ "Paar vontund Zeichenfolge ". Rufen Sie diesen Typ aufM t.

Mist ein Typkonstruktor:Mallein bezieht sich nicht auf einen Typ, sondernM _bezieht sich auf einen Typ, sobald Sie die Lücke mit einem Typ ausgefüllt haben. EinM intist ein Paar aus einem int und einem String. EinM stringist ein Paar aus einer Zeichenfolge und einer Zeichenfolge. Usw.

Herzlichen Glückwunsch, Sie haben eine Monade erstellt!

Formal ist Ihre Monade das Tupel<M, feed, wrap>.

Eine Monade ist ein Tupel<M, feed, wrap>wo:

  • Mist ein Typkonstruktor.
  • feednimmt eine (Funktion, die a nimmttund gibt eine zurückM u) und einM tund gibt eine zurückM u.
  • wrapnimmt einvund gibt eine zurückM v.

t, u, undvsind drei beliebige Typen, die gleich sein können oder nicht. Eine Monade erfüllt die drei Eigenschaften, die Sie für Ihre spezifische Monade bewiesen haben:

  • Fütternein eingewickelttin eine Funktion ist das gleiche wieVorbeigehendas ausgepacktetin die Funktion.

    Formal:feed(f, wrap(x)) = f(x)

  • Fütterung anM tinwraptut nichts mit demM t.

    Formal:feed(wrap, m) = m

  • Fütterung anM t(nennenm) in eine Funktion, die

    • passiert dieting
    • bekommt eineM u(nennenn) vong
    • Einspeisungenninf

    ist das gleiche wie

    • Fütterungming
    • bekommennvong
    • Fütterungninf

    Formal:feed(h, m) = feed(f, feed(g, m))woh(x) := feed(f, g(x))

In der Regelfeedwird genanntbind(AKA>>=in Haskell) undwrapwird genanntreturn.

Quelle
Translate

Ich werde versuchen, es zu erklärenMonadim Kontext von Haskell.

Bei der funktionalen Programmierung ist die Funktionszusammensetzung wichtig. Dadurch kann unser Programm aus kleinen, leicht lesbaren Funktionen bestehen.

Nehmen wir an, wir haben zwei Funktionen:g :: Int -> Stringundf :: String -> Bool.

Wir können es tun(f . g) x, was genauso ist wief (g x), woxist einIntWert.

Bei der Komposition / Anwendung des Ergebnisses einer Funktion auf eine andere ist es wichtig, dass die Typen übereinstimmen. Im obigen Fall der Typ des von zurückgegebenen Ergebnissesgmuss mit dem Typ identisch sein, der von akzeptiert wirdf.

Aber manchmal befinden sich Werte in Kontexten, und dies macht es etwas weniger einfach, Typen auszurichten. (Werte in Kontexten zu haben ist sehr nützlich. Zum Beispiel dieMaybe IntTyp steht fürIntWert, der möglicherweise nicht vorhanden ist, derIO StringTyp steht für aStringWert, der aufgrund einiger Nebenwirkungen vorhanden ist.)

Nehmen wir an, wir haben jetztg1 :: Int -> Maybe Stringundf1 :: String -> Maybe Bool. g1undf1sind sehr ähnlich zugundfbeziehungsweise.

Wir können nicht tun(f1 . g1) xoderf1 (g1 x), woxist einIntWert. Der Typ des von zurückgegebenen Ergebnissesg1ist nicht wasf1erwartet.

Wir könnten komponierenfundgmit dem.Operator, aber jetzt können wir nicht komponierenf1undg1mit.. Das Problem ist, dass wir einen Wert in einem Kontext nicht einfach an eine Funktion übergeben können, die einen Wert erwartet, der sich nicht in einem Kontext befindet.

Wäre es nicht schön, wenn wir einen Operator zum Komponieren vorstellen würden?g1undf1, so dass wir schreiben können(f1 OPERATOR g1) x? g1Gibt einen Wert in einem Kontext zurück. Der Wert wird aus dem Kontext genommen und auf angewendetf1. Und ja, wir haben einen solchen Operator. Es ist<=<.

Wir haben auch die>>=Operator, der für uns genau das Gleiche tut, allerdings in einer etwas anderen Syntax.

Wir schreiben:g1 x >>= f1. g1 xist einMaybe IntWert. Das>>=Betreiber hilft, das zu nehmenIntWert aus dem Kontext "Vielleicht nicht da" und wenden Sie ihn auf anf1. Das Ergebnis vonf1, die einMaybe Boolwird das Ergebnis des Ganzen sein>>=Betrieb.

Und schließlich, warum istMonadnützlich? weilMonadist die Typklasse, die die definiert>>=Betreiber, sehr ähnlich wie derEqTypklasse, die die definiert==und/=Betreiber.

Abschließend dieMonadTypklasse definiert die>>=Operator, mit dem wir Werte in einem Kontext (wir nennen diese monadischen Werte) an Funktionen übergeben können, die keine Werte in einem Kontext erwarten. Der Kontext wird gepflegt.

Wenn es hier eine Sache gibt, an die man sich erinnern muss, dann ist es dieseMonads ermöglichen eine Funktionszusammensetzung, die Werte in Kontexten beinhaltet.

Quelle
Translate

tl; dr

{-# LANGUAGE InstanceSigs #-}

newtype Id t = Id t

instance Monad Id where
   return :: t -> Id t
   return = Id

   (=<<) :: (a -> Id b) -> Id a -> Id b
   f =<< (Id x) = f x

Prolog

Der Anwendungsbetreiber$von Funktionen

forall a b. a -> b

ist kanonisch definiert

($) :: (a -> b) -> a -> b
f $ x = f x

infixr 0 $

in Bezug auf die Anwendung der Haskell-primitiven Funktionf x (infixl 10).

Komposition.ist definiert in$wie

(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \ x -> f $ g x

infixr 9 .

und erfüllt die Äquivalenzenforall f g h.

     f . id  =  f            :: c -> d   Right identity
     id . g  =  g            :: b -> c   Left identity
(f . g) . h  =  f . (g . h)  :: a -> d   Associativity

.ist assoziativ undidist seine rechte und linke Identität.

Das Kleisli Triple

Bei der Programmierung ist eine Monade ein Konstruktor vom Typ Funktor mit einer Instanz der Klasse vom Typ Monade. Es gibt mehrere äquivalente Varianten der Definition und Implementierung, die jeweils leicht unterschiedliche Intuitionen über die Monadenabstraktion enthalten.

Ein Funktor ist ein TypkonstruktorfVon Art* -> *mit einer Instanz der Funktortypklasse.

{-# LANGUAGE KindSignatures #-}

class Functor (f :: * -> *) where
   map :: (a -> b) -> (f a -> f b)

Zusätzlich zum Befolgen des statisch erzwungenen Typprotokolls müssen Instanzen der Funktortypklasse der Algebra entsprechenFunktorgesetze forall f g.

       map id  =  id           :: f t -> f t   Identity
map f . map g  =  map (f . g)  :: f a -> f c   Composition / short cut fusion

FunktorBerechnungenhabe den Typ

forall f t. Functor f => f t

Eine Berechnungc rbesteht ausErgebnisse rinnerhalbKontext c.

Unäre monadische Funktionen oderKleisli Pfeilehabe den Typ

forall m a b. Functor m => a -> m b

Kleisi-Pfeile sind Funktionen, die ein Argument annehmenaund eine monadische Berechnung zurückgebenm b.

Monaden werden kanonisch in Bezug auf die definiertKleisli dreifach forall m. Functor m =>

(m, return, (=<<))

implementiert als Typklasse

class Functor m => Monad m where
   return :: t -> m t
   (=<<)  :: (a -> m b) -> m a -> m b

infixr 1 =<<

DasKleisli Identität returnist ein Kleisli-Pfeil, der einen Wert förderttin monadischen Kontextm. ErweiterungoderKleisli Anwendung =<<wendet einen Kleisli-Pfeil ana -> m bzu Ergebnissen einer Berechnungm a.

Kleisli Komposition <=<wird in Bezug auf die Erweiterung definiert als

(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = \ x -> f =<< g x

infixr 1 <=<

<=<setzt zwei Kleisli-Pfeile zusammen und wendet den linken Pfeil auf die Ergebnisse der Anwendung des rechten Pfeils an.

Instanzen der Monadentypklasse müssen den Anweisungen entsprechenMonadengesetze, am elegantesten in Bezug auf die Kleisli-Komposition ausgedrückt:forall f g h.

   f <=< return  =  f                :: c -> m d   Right identity
   return <=< g  =  g                :: b -> m c   Left identity
(f <=< g) <=< h  =  f <=< (g <=< h)  :: a -> m d   Associativity

<=<ist assoziativ undreturnist seine rechte und linke Identität.

Identität

Der Identitätstyp

type Id t = t

ist die Identitätsfunktion für Typen

Id :: * -> *

Als Funktor interpretiert,

   return :: t -> Id t
=      id :: t ->    t

    (=<<) :: (a -> Id b) -> Id a -> Id b
=     ($) :: (a ->    b) ->    a ->    b

    (<=<) :: (b -> Id c) -> (a -> Id b) -> (a -> Id c)
=     (.) :: (b ->    c) -> (a ->    b) -> (a ->    c)

Im kanonischen Haskell wird die Identitätsmonade definiert

newtype Id t = Id t

instance Functor Id where
   map :: (a -> b) -> Id a -> Id b
   map f (Id x) = Id (f x)

instance Monad Id where
   return :: t -> Id t
   return = Id

   (=<<) :: (a -> Id b) -> Id a -> Id b
   f =<< (Id x) = f x

Möglichkeit

Ein Optionstyp

data Maybe t = Nothing | Just t

codiert die BerechnungMaybe tdas ergibt nicht unbedingt ein Ergebnist, Berechnung, die "fehlschlagen" kann. Die Option Monade ist definiert

instance Functor Maybe where
   map :: (a -> b) -> (Maybe a -> Maybe b)
   map f (Just x) = Just (f x)
   map _ Nothing  = Nothing

instance Monad Maybe where
   return :: t -> Maybe t
   return = Just

   (=<<) :: (a -> Maybe b) -> Maybe a -> Maybe b
   f =<< (Just x) = f x
   _ =<< Nothing  = Nothing

a -> Maybe bwird nur dann auf ein Ergebnis angewendet, wennMaybe aergibt ein Ergebnis.

newtype Nat = Nat Int

Die natürlichen Zahlen können als ganze Zahlen größer oder gleich Null codiert werden.

toNat :: Int -> Maybe Nat
toNat i | i >= 0    = Just (Nat i)
        | otherwise = Nothing

Die natürlichen Zahlen werden bei Subtraktion nicht geschlossen.

(-?) :: Nat -> Nat -> Maybe Nat
(Nat n) -? (Nat m) = toNat (n - m)

infixl 6 -?

Die Option Monade deckt eine Grundform der Ausnahmebehandlung ab.

(-? 20) <=< toNat :: Int -> Maybe Nat

Liste

Die Listenmonade über dem Listentyp

data [] t = [] | t : [t]

infixr 5 :

und seine additive Monoidoperation "anhängen"

(++) :: [t] -> [t] -> [t]
(x : xs) ++ ys = x : xs ++ ys
[]       ++ ys = ys

infixr 5 ++

codiertnichtlinearBerechnung[t]eine natürliche Menge ergeben0, 1, ...von Ergebnissent.

instance Functor [] where
   map :: (a -> b) -> ([a] -> [b])
   map f (x : xs) = f x : map f xs
   map _ []       = []

instance Monad [] where
   return :: t -> [t]
   return = (: [])

   (=<<) :: (a -> [b]) -> [a] -> [b]
   f =<< (x : xs) = f x ++ (f =<< xs)
   _ =<< []       = []

Erweiterung=<<verkettet++alle Listen[b]resultierend aus Anwendungenf xeines Kleisli Pfeilsa -> [b]zu Elementen von[a]in eine einzige Ergebnisliste[b].

Lassen Sie die richtigen Teiler einer positiven ganzen ZahlnSein

divisors :: Integral t => t -> [t]
divisors n = filter (`divides` n) [2 .. n - 1]

divides :: Integral t => t -> t -> Bool
(`divides` n) = (== 0) . (n `rem`)

dann

forall n.  let { f = f <=< divisors } in f n   =   []

Bei der Definition der Monadentypklasse anstelle der Erweiterung=<<verwendet der Haskell-Standard seinen Flip, denbindenOperator>>=.

class Applicative m => Monad m where
   (>>=) :: forall a b. m a -> (a -> m b) -> m b

   (>>) :: forall a b. m a -> m b -> m b
   m >> k = m >>= \ _ -> k
   {-# INLINE (>>) #-}

   return :: a -> m a
   return = pure

In dieser Erklärung wird der Einfachheit halber die Typklassenhierarchie verwendet

class              Functor f
class Functor m => Monad m

In Haskell ist die aktuelle Standardhierarchie

class                  Functor f
class Functor p     => Applicative p
class Applicative m => Monad m

denn nicht nur jede Monade ist ein Funktor, sondern jeder Applikativ ist ein Funktor und jede Monade ist auch ein Applikativ.

Unter Verwendung der Listenmonade wird der imperative Pseudocode verwendet

for a in (1, ..., 10)
   for b in (1, ..., 10)
      p <- a * b
      if even(p)
         yield p

grob übersetzt in dieblockiere,

do a <- [1 .. 10]
   b <- [1 .. 10]
   let p = a * b
   guard (even p)
   return p

das ÄquivalentMonadenverständnis,

[ p | a <- [1 .. 10], b <- [1 .. 10], let p = a * b, even p ]

und der Ausdruck

[1 .. 10] >>= (\ a ->
   [1 .. 10] >>= (\ b ->
      let p = a * b in
         guard (even p) >>       -- [ () | even p ] >>
            return p
      )
   )

Notation und Monadenverständnis sind syntaktischer Zucker für verschachtelte Bindungsausdrücke. Der Bindungsoperator wird für die lokale Namensbindung von monadischen Ergebnissen verwendet.

let x = v in e    =   (\ x -> e)  $  v   =   v  &  (\ x -> e)
do { r <- m; c }  =   (\ r -> c) =<< m   =   m >>= (\ r -> c)

wo

(&) :: a -> (a -> b) -> b
(&) = flip ($)

infixl 0 &

Die Schutzfunktion ist definiert

guard :: Additive m => Bool -> m ()
guard True  = return ()
guard False = fail

bei dem dieGerätetypoder "leeres Tupel"

data () = ()

Additive Monadendiese UnterstützungWahlundFehlerkann mit einer Typklasse abstrahiert werden

class Monad m => Additive m where
   fail  :: m t
   (<|>) :: m t -> m t -> m t

infixl 3 <|>

instance Additive Maybe where
   fail = Nothing

   Nothing <|> m = m
   m       <|> _ = m

instance Additive [] where
   fail = []
   (<|>) = (++)

wofailund<|>bilden ein Monoidforall k l m.

     k <|> fail  =  k
     fail <|> l  =  l
(k <|> l) <|> m  =  k <|> (l <|> m)

undfailist das absorbierende / vernichtende Nullelement von additiven Monaden

_ =<< fail  =  fail

Wenn in

guard (even p) >> return p

even pist wahr, dann produziert die Wache[()]und nach der Definition von>>, die lokale Konstantenfunktion

\ _ -> return p

wird auf das Ergebnis angewendet(). Wenn false, erstellt der Wächter die Listenmonadenfail(([]), was kein Ergebnis für die Anwendung eines Kleisli-Pfeils ergibt>>zu, also daspwird übersprungen.

Zustand

Monaden werden bekanntlich verwendet, um zustandsbehaftete Berechnungen zu codieren.

A Zustandsprozessorist eine Funktion

forall st t. st -> (t, st)

das übergeht einen Zustandstund ergibt ein Ergebnist. DasZustand stkann alles sein. Nichts, Flagge, Anzahl, Array, Handle, Maschine, Welt.

Der Typ der Statusprozessoren wird normalerweise aufgerufen

type State st t = st -> (t, st)

Die State-Processor-Monade ist die Arted* -> *FunktorState st. Kleisli-Pfeile der State-Processor-Monade sind Funktionen

forall st a b. a -> (State st) b

Im kanonischen Haskell ist die Lazy-Version der State Processor-Monade definiert

newtype State st t = State { stateProc :: st -> (t, st) }

instance Functor (State st) where
   map :: (a -> b) -> ((State st) a -> (State st) b)
   map f (State p) = State $ \ s0 -> let (x, s1) = p s0
                                     in  (f x, s1)

instance Monad (State st) where
   return :: t -> (State st) t
   return x = State $ \ s -> (x, s)

   (=<<) :: (a -> (State st) b) -> (State st) a -> (State st) b
   f =<< (State p) = State $ \ s0 -> let (x, s1) = p s0
                                     in  stateProc (f x) s1

Ein Statusprozessor wird ausgeführt, indem ein Anfangszustand bereitgestellt wird:

run :: State st t -> st -> (t, st)
run = stateProc

eval :: State st t -> st -> t
eval = fst . run

exec :: State st t -> st -> st
exec = snd . run

Der staatliche Zugang wird durch Grundelemente bereitgestelltgetundput, Methoden der Abstraktion vorbeiStaatsbürgerlichMonaden:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class Monad m => Stateful m st | m -> st where
   get :: m st
   put :: st -> m ()

m -> sterklärt afunktionale Abhängigkeitvom Zustandstypstauf der Monadem;; dass aState tbestimmt beispielsweise den Zustandstypteinzigartig.

instance Stateful (State st) st where
   get :: State st st
   get = State $ \ s -> (s, s)

   put :: st -> State st ()
   put s = State $ \ _ -> ((), s)

mit dem Gerätetyp analog zuvoidin C.

modify :: Stateful m st => (st -> st) -> m ()
modify f = do
   s <- get
   put (f s)

gets :: Stateful m st => (st -> t) -> m t
gets f = do
   s <- get
   return (f s)

getswird häufig mit Datensatzfeld-Accessoren verwendet.

Das Zustandsmonadenäquivalent des variablen Threadings

let s0 = 34
    s1 = (+ 1) s0
    n = (* 12) s1
    s2 = (+ 7) s1
in  (show n, s2)

wos0 :: Intist die gleichermaßen referenziell transparente, aber unendlich elegantere und praktischere

(flip run) 34
   (do
      modify (+ 1)
      n <- gets (* 12)
      modify (+ 7)
      return (show n)
   )

modify (+ 1)ist eine Berechnung des TypsState Int (), außer für seinebewirkengleichwertigreturn ().

(flip run) 34
   (modify (+ 1) >>
      gets (* 12) >>= (\ n ->
         modify (+ 7) >>
            return (show n)
      )
   )

Das Monadengesetz der Assoziativität kann in Bezug auf geschrieben werden>>= forall m f g.

(m >>= f) >>= g  =  m >>= (\ x -> f x >>= g)

or

do {                 do {                   do {
   r1 <- do {           x <- m;                r0 <- m;
      r0 <- m;   =      do {            =      r1 <- f r0;
      f r0                 r1 <- f x;          g r1
   };                      g r1             }
   g r1                 }
}                    }

Wie bei der ausdrucksorientierten Programmierung (z. B. Rust) repräsentiert die letzte Anweisung eines Blocks seine Ausbeute. Der Bindungsoperator wird manchmal als "programmierbares Semikolon" bezeichnet.

Iterationskontrollstrukturprimitive aus der strukturierten imperativen Programmierung werden monadisch emuliert

for :: Monad m => (a -> m b) -> [a] -> m ()
for f = foldr ((>>) . f) (return ())

while :: Monad m => m Bool -> m t -> m ()
while c m = do
   b <- c
   if b then m >> while c m
        else return ()

forever :: Monad m => m t
forever m = m >> forever m

Input-Output

data World

Die I / O-Weltstaatsprozessor-Monade ist eine Versöhnung von reinem Haskell und der realen Welt, von funktionaler denotativer und zwingender operativer Semantik. Ein enges Analogon zur tatsächlichen strengen Umsetzung:

type IO t = World -> (t, World)

Die Interaktion wird durch unreine Grundelemente erleichtert

getChar         :: IO Char
putChar         :: Char -> IO ()
readFile        :: FilePath -> IO String
writeFile       :: FilePath -> String -> IO ()
hSetBuffering   :: Handle -> BufferMode -> IO ()
hTell           :: Handle -> IO Integer
. . .              . . .

Die Verunreinigung des verwendeten CodesIOGrundelemente werden vom Typsystem permanent protokolliert. Weil Reinheit großartig ist, was passiert inIObleibt inIO.

unsafePerformIO :: IO t -> t

Oder sollte es zumindest.

Die Typensignatur eines Haskell-Programms

main :: IO ()
main = putStrLn "Hello, World!"

erweitert sich zu

World -> ((), World)

Eine Funktion, die eine Welt verändert.

Epilog

Die Kategorie, deren Objekte Haskell-Typen sind und deren Morphismen Funktionen zwischen Haskell-Typen sind, ist die Kategorie „schnell und locker“Hask.

Ein FunktorTist eine Zuordnung aus einer KategorieCzu einer KategorieD;; für jedes Objekt inCein Objekt inD

Tobj :  Obj(C) -> Obj(D)
   f :: *      -> *

und für jeden Morphismus inCein Morphismus inD

Tmor :  HomC(X, Y) -> HomD(Tobj(X), Tobj(Y))
 map :: (a -> b)   -> (f a -> f b)

woX, Ysind Objekte inC. HomC(X, Y)ist derHomomorphismusklassealler MorphismenX -> YimC. Der Funktor muss die Identität und Zusammensetzung des Morphismus bewahren, die „Struktur“ vonC, imD.

                    Tmor    Tobj

      T(id)  =  id        : T(X) -> T(X)   Identity
T(f) . T(g)  =  T(f . g)  : T(X) -> T(Z)   Composition

DasKleisli Kategorieeiner KategorieCwird von einem Kleisli Triple gegeben

<T, eta, _*>

eines Endofunktors

T : C -> C

(f), ein Identitätsmorphismuseta (return) und einen Erweiterungsoperator* (=<<).

Jeder Kleisli-Morphismus inHask

      f :  X -> T(Y)
      f :: a -> m b

vom Nebenstellenbetreiber

   (_)* :  Hom(X, T(Y)) -> Hom(T(X), T(Y))
  (=<<) :: (a -> m b)   -> (m a -> m b)

wird ein Morphismus in gegebenHaskKleisli Kategorie

     f* :  T(X) -> T(Y)
(f =<<) :: m a  -> m b

Komposition in der Kategorie Kleisli.Twird in Bezug auf die Erweiterung angegeben

 f .T g  =  f* . g       :  X -> T(Z)
f <=< g  =  (f =<<) . g  :: a -> m c

und befriedigt dieKategorie Axiome

       eta .T g  =  g                :  Y -> T(Z)   Left identity
   return <=< g  =  g                :: b -> m c

       f .T eta  =  f                :  Z -> T(U)   Right identity
   f <=< return  =  f                :: c -> m d

  (f .T g) .T h  =  f .T (g .T h)    :  X -> T(U)   Associativity
(f <=< g) <=< h  =  f <=< (g <=< h)  :: a -> m d

welche unter Anwendung der Äquivalenztransformationen

     eta .T g  =  g
     eta* . g  =  g               By definition of .T
     eta* . g  =  id . g          forall f.  id . f  =  f
         eta*  =  id              forall f g h.  f . h  =  g . h  ==>  f  =  g

(f .T g) .T h  =  f .T (g .T h)
(f* . g)* . h  =  f* . (g* . h)   By definition of .T
(f* . g)* . h  =  f* . g* . h     . is associative
    (f* . g)*  =  f* . g*         forall f g h.  f . h  =  g . h  ==>  f  =  g

in Bezug auf die Erweiterung sind kanonisch gegeben

               eta*  =  id                 :  T(X) -> T(X)   Left identity
       (return =<<)  =  id                 :: m t -> m t

           f* . eta  =  f                  :  Z -> T(U)      Right identity
   (f =<<) . return  =  f                  :: c -> m d

          (f* . g)*  =  f* . g*            :  T(X) -> T(Z)   Associativity
(((f =<<) . g) =<<)  =  (f =<<) . (g =<<)  :: m a -> m c

Monaden können auch nicht als Kleislsche Erweiterung, sondern als natürliche Transformation definiert werdenmu, in der Programmierung aufgerufenjoin. Eine Monade ist definiert alsmuals Triple über eine KategorieCeines Endofunktors

     T :  C -> C
     f :: * -> *

und zwei natürliche Transformationen

   eta :  Id -> T
return :: t  -> f t

    mu :  T . T   -> T
  join :: f (f t) -> f t

Befriedigung der Äquivalenzen

       mu . T(mu)  =  mu . mu               :  T . T . T -> T . T   Associativity
  join . map join  =  join . join           :: f (f (f t)) -> f t

      mu . T(eta)  =  mu . eta       =  id  :  T -> T               Identity
join . map return  =  join . return  =  id  :: f t -> f t

Die Monadentypklasse wird dann definiert

class Functor m => Monad m where
   return :: t -> m t
   join   :: m (m t) -> m t

Das KanonischemuImplementierung der Option Monade:

instance Monad Maybe where
   return = Just

   join (Just m) = m
   join Nothing  = Nothing

DasconcatFunktion

concat :: [[a]] -> [a]
concat (x : xs) = x ++ concat xs
concat []       = []

ist derjoinder Liste Monade.

instance Monad [] where
   return :: t -> [t]
   return = (: [])

   (=<<) :: (a -> [b]) -> ([a] -> [b])
   (f =<<) = concat . map f

Implementierungen vonjoinkann mit der Äquivalenz aus dem Erweiterungsformular übersetzt werden

     mu  =  id*           :  T . T -> T
   join  =  (id =<<)      :: m (m t) -> m t

Die umgekehrte Übersetzung vonmuzur Erweiterungsform ist gegeben durch

     f*  =  mu . T(f)     :  T(X) -> T(Y)
(f =<<)  =  join . map f  :: m a -> m b

Aber warum sollte eine so abstrakte Theorie für die Programmierung von Nutzen sein?

Die Antwort ist einfach: Als Informatiker sind wirWertabstraktion! Wenn wir die Schnittstelle zu einer Softwarekomponente entwerfen, werden wirwollenes soll so wenig wie möglich über die Umsetzung verraten. Wir möchten in der Lage sein, die Implementierung durch viele Alternativen zu ersetzen, viele andere "Instanzen" desselben "Konzepts". Wenn wir eine generische Schnittstelle zu vielen Programmbibliotheken entwerfen, ist es noch wichtiger, dass die von uns ausgewählte Schnittstelle eine Vielzahl von Implementierungen aufweist. Es ist die Allgemeinheit des Monadenkonzepts, die wir so hoch schätzenweilDie Kategorietheorie ist so abstrakt, dass ihre Konzepte für die Programmierung so nützlich sind.

Es ist daher nicht verwunderlich, dass die Verallgemeinerung der Monaden, die wir unten präsentieren, auch einen engen Zusammenhang mit der Kategorietheorie hat. Wir betonen jedoch, dass unser Zweck sehr praktisch ist: Es geht nicht darum, die Kategorietheorie zu implementieren, sondern einen allgemeineren Weg zu finden, um Kombinatorbibliotheken zu strukturieren. Es ist einfach unser Glück, dass Mathematiker bereits einen Großteil der Arbeit für uns geleistet haben!

vonVerallgemeinerung von Monaden auf Pfeilevon John Hughes

Quelle
Yale Lee
Translate

Was die Welt braucht, ist ein weiterer Monaden-Blog-Beitrag, aber ich denke, dies ist nützlich, um bestehende Monaden in freier Wildbahn zu identifizieren.

Sierpinski triangle

Das Obige ist ein Fraktal namens Sierpinski-Dreieck, das einzige Fraktal, an das ich mich erinnern kann, es gezeichnet zu haben. Fraktale sind selbstähnliche Strukturen wie das obige Dreieck, bei denen die Teile dem Ganzen ähnlich sind (in diesem Fall genau die Hälfte der Skala als übergeordnetes Dreieck).

Monaden sind Fraktale. Bei einer monadischen Datenstruktur können ihre Werte zusammengesetzt werden, um einen anderen Wert der Datenstruktur zu bilden. Aus diesem Grund ist es für die Programmierung nützlich, und aus diesem Grund tritt es in vielen Situationen auf.

Quelle
Translate

http://code.google.com/p/monad-tutorial/ist in Arbeit, um genau diese Frage zu beantworten.

Quelle
Translate

Eine Monade wird verwendet, um Objekte zu kapseln, deren Status sich ändert. Es tritt am häufigsten in Sprachen auf, in denen Sie sonst keinen veränderbaren Status haben (z. B. Haskell).

Ein Beispiel wäre für Datei-E / A.

Sie können eine Monade für Datei-E / A verwenden, um die sich ändernde Statusnatur nur auf den Code zu isolieren, der die Monade verwendet hat. Der Code innerhalb der Monade kann den sich ändernden Zustand der Welt außerhalb der Monade effektiv ignorieren - dies macht es viel einfacher, über die Gesamtwirkung Ihres Programms nachzudenken.

Quelle
Translate

Lassen Sie das unten "{| a |m}"stellen ein Stück monadischer Daten dar. Ein Datentyp, der für eina:

        (I got an a!)
          /        
    {| a |m}

Funktion,fweiß, wie man eine Monade erstellt, wenn sie nur eine hättea:

       (Hi f! What should I be?)
                      /
(You?. Oh, you'll be /
 that data there.)  /
 /                 /  (I got a b.)
|    --------------      |
|  /                     |
f a                      |
  |--later->       {| b |m}

Hier sehen wir Funktion,f, versucht eine Monade zu bewerten, wird aber zurechtgewiesen.

(Hmm, how do I get that a?)
 o       (Get lost buddy.
o         Wrong type.)
o       /
f {| a |m}

Funktion,f, findet einen Weg, die zu extrahierenadurch die Nutzung>>=.

        (Muaahaha. How you 
         like me now!?)       
    (Better.)      \
        |     (Give me that a.)
(Fine, well ok.)    |
         \          |
   {| a |m}   >>=   f

Wenig tutfwissen, die Monade und>>=sind in Absprache.

            (Yah got an a for me?)       
(Yeah, but hey    | 
 listen. I got    |
 something to     |
 tell you first   |
 ...)   \        /
         |      /
   {| a |m}   >>=   f

Aber worüber reden sie eigentlich? Nun, das hängt von der Monade ab. Nur abstrakt zu sprechen hat nur begrenzten Nutzen. Sie müssen einige Erfahrungen mit bestimmten Monaden haben, um das Verständnis zu konkretisieren.

Zum Beispiel der Datentyp Vielleicht

 data Maybe a = Nothing | Just a

hat eine Monadeninstanz, die sich wie folgt verhält ...

Worin, wenn der Fall istJust a

            (Yah what is it?)       
(... hm? Oh,      |
forget about it.  |
Hey a, yr up.)    | 
            \     |
(Evaluation  \    |
time already? \   |
Hows my hair?) |  |
      |       /   |
      |  (It's    |
      |  fine.)  /
      |   /     /    
   {| a |m}   >>=   f

Aber für den Fall vonNothing

        (Yah what is it?)       
(... There      |
is no a. )      |
  |        (No a?)
(No a.)         |
  |        (Ok, I'll deal
  |         with this.)
   \            |
    \      (Hey f, get lost.) 
     \          |   ( Where's my a? 
      \         |     I evaluate a)
       \    (Not any more  |
        \    you don't.    |
         |   We're returning
         |   Nothing.)   /
         |      |       /
         |      |      /
         |      |     /
   {| a |m}   >>=   f      (I got a b.)
                    |  (This is   \
                    |   such a     \
                    |   sham.) o o  \
                    |               o|
                    |--later-> {| b |m}

Die Vielleicht-Monade lässt also eine Berechnung fortsetzen, wenn sie tatsächlich die enthältaes wirbt, aber bricht die Berechnung ab, wenn es nicht tut. Das Ergebnis ist jedoch immer noch ein Stück monadischer Daten, jedoch nicht die Ausgabe vonf. Aus diesem Grund wird die Vielleicht-Monade verwendet, um den Kontext des Scheiterns darzustellen.

Verschiedene Monaden verhalten sich unterschiedlich. Listen sind andere Datentypen mit monadischen Instanzen. Sie verhalten sich wie folgt:

(Ok, here's your a. Well, its
 a bunch of them, actually.)
  |
  |    (Thanks, no problem. Ok
  |     f, here you go, an a.)
  |       |
  |       |        (Thank's. See
  |       |         you later.)
  |  (Whoa. Hold up f,      |
  |   I got another         |
  |   a for you.)           |
  |       |      (What? No, sorry.
  |       |       Can't do it. I 
  |       |       have my hands full
  |       |       with all these "b" 
  |       |       I just made.) 
  |  (I'll hold those,      |
  |   you take this, and   /
  |   come back for more  /
  |   when you're done   / 
  |   and we'll do it   / 
  |   again.)          /
   \      |  ( Uhhh. All right.)
    \     |       /    
     \    \      /
{| a |m}   >>=  f  

In diesem Fall wusste die Funktion, wie eine Liste aus ihrer Eingabe erstellt wird, wusste jedoch nicht, was mit zusätzlichen Eingaben und zusätzlichen Listen zu tun ist. Die Bindung>>=halffdurch Kombinieren der mehreren Ausgänge. Ich füge dieses Beispiel hinzu, um dies zu zeigen>>=ist für das Extrahieren verantwortlichahat es auch Zugriff auf die eventuell gebundene Ausgabe vonf. In der Tat wird es niemals welche extrahierenaes sei denn, es weiß, dass die endgültige Ausgabe den gleichen Kontexttyp hat.

Es gibt andere Monaden, die verwendet werden, um unterschiedliche Kontexte darzustellen. Hier sind einige weitere Charakterisierungen. DasIOMonade hat eigentlich keinea, aber es kennt einen Kerl und wird das bekommenafür dich. DasState stMonade hat einen geheimen Vorrat vonstdass es an übergeben wirdfobwohl unter dem Tischfkam gerade und fragte nach einema. DasReader rMonade ist ähnlich wieState st, obwohl es nur lässtfansehenr.

Der Punkt bei all dem ist, dass jede Art von Daten, die selbst als Monade deklariert werden, einen Kontext zum Extrahieren eines Werts aus der Monade deklariert. Der große Gewinn von all dem? Nun, es ist einfach genug, eine Berechnung mit einem Kontext zu formulieren. Es kann jedoch unordentlich werden, wenn mehrere kontextreiche Berechnungen aneinandergereiht werden. Die Monadenoperationen sorgen dafür, dass die Interaktionen des Kontexts aufgelöst werden, damit der Programmierer dies nicht muss.

Beachten Sie, dass die Verwendung der>>=Erleichtert das Durcheinander, indem ein Teil der Autonomie weggenommen wirdf. Das heißt, im obigen Fall vonNothingzum Beispiel,fkann nicht mehr entscheiden, was im Fall von zu tun istNothing;; es ist in verschlüsselt>>=. Dies ist der Kompromiss. Wenn es nötig wäre fürfzu entscheiden, was im Fall von zu tun istNothing, dannfsollte eine Funktion von gewesen seinMaybe azuMaybe b. In diesem Fall,Maybeeine Monade zu sein ist irrelevant.

Beachten Sie jedoch, dass ein Datentyp manchmal seine Konstruktoren nicht exportiert (siehe E / A). Wenn wir mit dem angegebenen Wert arbeiten möchten, haben wir keine andere Wahl, als mit seiner monadischen Schnittstelle zu arbeiten.

Quelle