Ich bin neu in der funktionalen Programmierung (komme von Javascript), und es fällt mir schwer, den Unterschied zwischen den beiden zu erklären, was auch mit meinem Verständnis von Funktoren vs. Monaden zu tun hat.
Funktion:
%Vor%Monade (vereinfacht):
%Vor%fmap
verwendet eine Funktion und einen Funktor und gibt einen Funktor zurück. >>=
nimmt eine Funktion und eine Monade und gibt eine Monade zurück. Der Unterschied zwischen den beiden ist im Funktionsparameter:
fmap
- (a -> b)
>>=
- (a -> m b)
>>=
verwendet einen Funktionsparameter, der eine Monade zurückgibt. Ich weiß, dass das wichtig ist, aber ich habe Schwierigkeiten zu sehen, wie dieses eine kleine Ding Monaden viel mächtiger macht als Funktoren. Kann jemand erklären?
Nun, (<$>)
ist ein Alias für fmap
und (=<<)
ist identisch mit (>>=)
mit den vertauschten Argumenten:
Der Unterschied ist jetzt ziemlich klar: Mit der Bindefunktion wenden wir eine Funktion an, die ein b y
anstatt eines y
zurückgibt. Welchen Unterschied macht das?
Betrachten Sie dieses kleine Beispiel:
%Vor% Beachten Sie, dass (<$>)
foo
auf 3
anwendet und das Ergebnis in ein Just
zurückversetzt. Mit anderen Worten, das Ergebnis dieser Berechnung kann nicht Nothing
sein. Im Gegenteil:
Diese Berechnung kann Nothing
zurückgeben. (Zum Beispiel wird bar x = Nothing
es tun.)
Wir können eine ähnliche Sache mit der Listenmonade machen:
%Vor% Kurz gesagt, mit (<$>)
(d. h. fmap
) ist die "Struktur" des Ergebnisses immer identisch mit der Eingabe. Aber mit (=<<)
(d. H.% Co_de%) kann sich die Struktur des Ergebnisses ändern. Dies ermöglicht die bedingte Ausführung, Reaktion auf Eingabe und eine ganze Reihe anderer Dinge.
Eine kurze Antwort ist, dass wenn Sie m (m a)
in m a
auf eine Weise umwandeln können, die sinnvoll ist, dann ist es eine Monade. Dies ist für alle Monaden möglich, aber nicht unbedingt für Funktoren.
Ich denke, die verwirrendste Sache ist, dass alle gängigen Beispiele von Funktoren (z. B. List
, Maybe
, IO
) ebenfalls Monaden sind. Wir brauchen ein Beispiel für etwas, das ein Functor ist, aber kein Monad.
Ich werde ein Beispiel aus einem hypothetischen Kalenderprogramm verwenden. Der folgende Code definiert einen Event
Functor, der einige Daten speichert, die zu dem Ereignis und der Zeit gehören, zu der es auftritt.
Das Objekt Event
speichert die Uhrzeit, zu der das Ereignis eintritt, und einige zusätzliche Daten, die mit fmap
geändert werden können. Versuchen wir nun, es zu einer Monade zu machen:
Wir finden, dass wir das nicht können, weil Sie zwei LocalTime
-Objekte haben werden. timeA
von der angegebenen Event
und timeB
von Event
, gegeben durch das Ergebnis von f a
. Unser Event
-Typ ist so definiert, dass er nur ein LocalTime
( time
) hat, an dem er auftritt, und daher ist es nicht möglich, eine Monade zu erstellen, ohne zwei LocalTime
s in eins zu verwandeln. (Es kann einen Fall geben, in dem dies Sinn machen könnte und du könntest dies in eine Monade verwandeln, wenn du es wirklich willst).
Nehmen wir an, dass IO
nur ein Functor
und nicht Monad
sind. Wie könnten wir zwei Aktionen abfolgen? Sagen wir, wie getChar :: IO Char
und putChar :: Char -> IO ()
.
Wir könnten versuchen, über getChar
(eine Aktion, die, wenn sie ausgeführt wird, Char
von stdin liest) mit putChar
zu mappen.
Nun haben wir ein Programm, das, wenn es ausgeführt wird, ein Char
von stdin liest und ein Programm erzeugt, das, wenn es ausgeführt wird, Char
nach stdout schreibt. Aber was wir eigentlich wollen, ist ein Programm, das, wenn es ausgeführt wird, ein Char
von stdin liest und das Char
nach stdout schreibt. Also brauchen wir eine Funktion "flattern" (in der IO
case, "sequencing") mit type:
Functor
selbst bietet diese Funktion nicht. Aber es ist eine Funktion von Monad
, wo es den allgemeineren Typ hat:
Was hat das alles mit >>=
zu tun? Zufällig ist monadische Bindung nur eine Kombination aus fmap
und join
:
Eine weitere Möglichkeit, den Unterschied zu sehen, ist, dass fmap
niemals die Gesamtstruktur des gemappten Wertes ändert, aber join
(und daher auch >>=
) kann das tun.
In IO
-Aktionen bewirkt fmap
nie zusätzliche Lese- / Schreibvorgänge oder andere Effekte. Aber join
sequenziert das Lesen / Schreiben der inneren Aktion nach denen der äußeren Aktion.
Tags und Links haskell functional-programming monads functor