Es ist eigentlich nur ein normaler Datenkonstruktor, der zufällig im Prelude definiert wird. Dies ist die Standardbibliothek, die automatisch in jedes Modul importiert wird.
Was vielleicht strukturell ist
Die Definition sieht ungefähr so aus:
data Maybe a = Just a
| Nothing
Diese Deklaration definiert einen Typ, Maybe a
der durch eine Typvariable parametrisiert wird. a
Dies bedeutet lediglich, dass Sie ihn mit jedem Typ anstelle von verwenden können a
.
Konstruieren und zerstören
Der Typ hat zwei Konstruktoren Just a
und Nothing
. Wenn ein Typ mehrere Konstruktoren hat, bedeutet dies, dass ein Wert des Typs mit nur einem der möglichen Konstruktoren erstellt worden sein muss. Für diesen Typ wurde ein Wert entweder über Just
oder erstellt Nothing
, es gibt keine anderen (fehlerfreien) Möglichkeiten.
Da Nothing
es keinen Parametertyp gibt, benennt es bei Verwendung als Konstruktor einen konstanten Wert, der Maybe a
für alle Typen vom Typ type ist a
. Der Just
Konstruktor verfügt jedoch über einen Typparameter. Wenn er als Konstruktor verwendet wird, verhält er sich wie eine Funktion von Typ a
bis Maybe a
, dh er hat den Typa -> Maybe a
Die Konstruktoren eines Typs erstellen also einen Wert dieses Typs. Die andere Seite der Dinge ist, wenn Sie diesen Wert verwenden möchten, und hier kommt der Mustervergleich ins Spiel. Im Gegensatz zu Funktionen können Konstruktoren in Musterbindungsausdrücken verwendet werden. Auf diese Weise können Sie Fallanalysen von Werten durchführen, die zu Typen mit mehr als einem Konstruktor gehören.
Um einen Maybe a
Wert in einer Musterübereinstimmung zu verwenden, müssen Sie für jeden Konstruktor ein Muster bereitstellen, wie folgt:
case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
In diesem Fall würde das erste Muster übereinstimmen, wenn der Wert wäre Nothing
, und das zweite würde übereinstimmen, wenn der Wert mit konstruiert wurde Just
. Wenn der zweite übereinstimmt, bindet er auch den Namen val
an den Parameter, der an den Just
Konstruktor übergeben wurde, als der Wert erstellt wurde, mit dem Sie übereinstimmen.
Was vielleicht bedeutet
Vielleicht waren Sie bereits damit vertraut, wie das funktionierte; Maybe
Werte sind nicht wirklich magisch , sondern nur ein normaler alaskischer Haskell-Datentyp (ADT). Aber es wird ziemlich oft verwendet, weil es einen Typ, wie Integer
aus Ihrem Beispiel, effektiv in einen neuen Kontext "hebt" oder erweitert, in dem es einen zusätzlichen Wert ( Nothing
) hat, der einen Wertmangel darstellt! Das Typsystem erfordert dann , dass Sie für diesen Mehrwert zu überprüfen , bevor es Sie an dem erhalten lassen , Integer
die vielleicht da sein. Dies verhindert eine bemerkenswerte Anzahl von Fehlern.
Viele Sprachen verarbeiten diese Art von "No-Value" -Wert heutzutage über NULL-Referenzen. Tony Hoare, ein bedeutender Informatiker (er hat Quicksort erfunden und ist Turing-Preisträger), hält dies für seinen "Milliarden-Dollar-Fehler" . Der Vielleicht-Typ ist nicht der einzige Weg, dies zu beheben, aber er hat sich als effektiver Weg erwiesen, dies zu tun.
Vielleicht als Funktor
Die Idee, einen Typ in einen anderen zu transformieren, sodass Operationen für den alten Typ auch so transformiert werden können, dass sie für den neuen Typ funktionieren, ist das Konzept hinter der aufgerufenen Haskell-Typklasse Functor
, die Maybe a
eine nützliche Instanz von hat.
Functor
stellt eine aufgerufene Methode bereit fmap
, die Funktionen, die über Werte vom Basistyp (z. B. Integer
) liegen, Funktionen zuordnet, die über Werte vom angehobenen Typ (z. B. Maybe Integer
) reichen . Eine Funktion, die transformiert wurde fmap
, um an einem Maybe
Wert zu arbeiten, funktioniert folgendermaßen:
case maybeVal of
Nothing -> Nothing -- there is nothing, so just return Nothing
Just val -> Just (f val) -- there is a value, so apply the function to it
Wenn Sie also einen Maybe Integer
Wert m_x
und eine Int -> Int
Funktion haben f
, können Sie fmap f m_x
die Funktion f
direkt auf die anwenden , Maybe Integer
ohne sich Sorgen machen zu müssen, ob sie tatsächlich einen Wert hat oder nicht. Tatsächlich können Sie eine ganze Kette von angehobenen Integer -> Integer
Funktionen auf Maybe Integer
Werte anwenden und müssen sich nur noch Nothing
einmal explizit darum kümmern, wenn Sie fertig sind.
Vielleicht als Monade
Ich bin mir nicht sicher, wie vertraut Sie mit dem Konzept eines sind Monad
, aber Sie haben es zumindest schon einmal verwendet IO a
, und die Typensignatur IO a
sieht bemerkenswert ähnlich aus Maybe a
. Obwohl IO
es insofern besonders ist, als es seine Konstruktoren nicht für Sie verfügbar macht und daher nur vom Haskell-Laufzeitsystem "ausgeführt" werden kann, ist es Functor
neben dem auch noch ein Monad
. In der Tat gibt es einen wichtigen Sinn, in dem a Monad
nur eine besondere Art Functor
mit einigen zusätzlichen Funktionen ist, aber dies ist nicht der richtige Ort, um darauf einzugehen.
Wie auch immer, Monaden mögen IO
Zuordnungstypen zu neuen Typen, die "Berechnungen, die zu Werten führen" darstellen, und Sie können Funktionen Monad
über eine sehr fmap
ähnliche Funktion liftM
, die eine reguläre Funktion in eine "Berechnung umwandelt, die zu dem Wert führt, der durch Auswertung der Werte erhalten wird, in Typen umwandeln Funktion."
Sie haben wahrscheinlich erraten (wenn Sie so weit gelesen haben), dass dies Maybe
auch ein ist Monad
. Es stellt "Berechnungen dar, bei denen möglicherweise kein Wert zurückgegeben wird". Genau wie im fmap
Beispiel können Sie so eine ganze Reihe von Berechnungen durchführen, ohne nach jedem Schritt explizit nach Fehlern suchen zu müssen. Tatsächlich wird bei der Art und Weise, wie die Monad
Instanz aufgebaut ist, eine Berechnung der Maybe
Werte gestoppt , sobald eine gefunden Nothing
wird. Dies ist also wie ein sofortiger Abbruch oder eine wertlose Rückkehr mitten in einer Berechnung.
Du hättest vielleicht schreiben können
Wie ich bereits sagte, ist dem Maybe
Typ, der in die Sprachsyntax oder das Laufzeitsystem integriert ist, nichts inhärent . Wenn Haskell es nicht standardmäßig zur Verfügung gestellt hat, können Sie alle Funktionen selbst bereitstellen! Tatsächlich könnten Sie es trotzdem selbst mit unterschiedlichen Namen erneut schreiben und die gleiche Funktionalität erhalten.
Hoffentlich verstehen Sie den Maybe
Typ und seine Konstruktoren jetzt, aber wenn noch etwas unklar ist, lassen Sie es mich wissen!
Maybe
wo andere Sprachen verwenden würdennull
odernil
(mit bösenNullPointerException
lauern in jeder Ecke). Jetzt verwenden auch andere Sprachen dieses Konstrukt: Scala asOption
und sogar Java 8 haben denOptional
Typ.