Haskell Language
Monaden
Suche…
Einführung
Eine Monade ist ein Datentyp zusammenstellbarer Aktionen. Monad ist die Klasse von Typkonstruktoren, deren Werte solche Aktionen darstellen. Vielleicht ist IO das am besten erkennbare: Ein Wert von IO a ist ein "Rezept zum Abrufen a Wertes aus der realen Welt".
Wir sagen, ein Typkonstruktor m (wie [] oder Maybe ) bildet eine Monade, wenn es eine instance Monad m gibt, die bestimmte Gesetze über die Zusammenstellung von Aktionen erfüllt. Wir können dann über ma als eine "Aktion, deren Ergebnis den Typ a " denken.
Die vielleicht Monade
Maybe verwendet wird möglicherweise leere Werte darzustellen - ähnlich wie null in anderen Sprachen. Normalerweise wird es als Ausgabetyp von Funktionen verwendet, die auf irgendeine Weise versagen können.
Betrachten Sie die folgende Funktion:
halve :: Int -> Maybe Int
halve x
| even x = Just (x `div` 2)
| odd x = Nothing
Stellen Sie sich halve als eine Aktion vor, die von einem Int , das versucht, die ganze Zahl zu halbieren, und versagt, wenn sie ungerade ist.
Wie halve wir eine ganze Zahl dreimal?
takeOneEighth :: Int -> Maybe Int -- (after you read the 'do' sub-section:)
takeOneEighth x =
case halve x of -- do {
Nothing -> Nothing
Just oneHalf -> -- oneHalf <- halve x
case halve oneHalf of
Nothing -> Nothing
Just oneQuarter -> -- oneQuarter <- halve oneHalf
case halve oneQuarter of
Nothing -> Nothing -- oneEighth <- halve oneQuarter
Just oneEighth ->
Just oneEighth -- return oneEighth }
-
takeOneEighthist eine Abfolge von dreihalveSchritten, die miteinander verkettet sind. - Wenn ein
halvefehlschlägt, möchten wir, dass die gesamte KompositiontakeOneEighthfehlschlägt. - Wenn eine
halveStufe erfolgreich ist, möchten wir das Ergebnis weiterleiten.
instance Monad Maybe where
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= f = Nothing -- infixl 1 >>=
Just x >>= f = Just (f x) -- also, f =<< m = m >>= f
-- return :: a -> Maybe a
return x = Just x
und jetzt können wir schreiben:
takeOneEighth :: Int -> Maybe Int
takeOneEighth x = halve x >>= halve >>= halve -- or,
-- return x >>= halve >>= halve >>= halve -- which is parsed as
-- (((return x) >>= halve) >>= halve) >>= halve -- which can also be written as
-- (halve =<<) . (halve =<<) . (halve =<<) $ return x -- or, equivalently, as
-- halve <=< halve <=< halve $ x
Kleisli-Zusammensetzung <=< ist definiert als (g <=< f) x = g =<< fx oder äquivalent als (f >=> g) x = fx >>= g . Damit wird die obige Definition gerecht
takeOneEighth :: Int -> Maybe Int
takeOneEighth = halve <=< halve <=< halve -- infixr 1 <=<
-- or, equivalently,
-- halve >=> halve >=> halve -- infixr 1 >=>
Es gibt drei Monadengesetze, die von jeder Monade befolgt werden sollten, dh jeder Typ, der eine Instanz der Monad Typklasse darstellt:
1. return x >>= f = f x
2. m >>= return = m
3. (m >>= g) >>= h = m >>= (\y -> g y >>= h)
wobei m eine Monade ist, hat f den Typ a -> mb und g den Typ b -> mc .
Oder gleichwertig mit dem oben definierten Kompositionsoperator >=> Kleisli:
1. return >=> g = g -- do { y <- return x ; g y } == g x
2. f >=> return = f -- do { y <- f x ; return y } == f x
3. (f >=> g) >=> h = f >=> (g >=> h) -- do { z <- do { y <- f x; g y } ; h z }
-- == do { y <- f x ; do { z <- g y; h z } }
Das Befolgen dieser Gesetze macht es viel einfacher, über die Monade zu argumentieren, weil sie garantiert, dass die Verwendung monadischer Funktionen und das Komponieren dieser Funktionen sich auf vernünftige Weise verhalten, ähnlich wie andere Monaden.
Lassen Sie uns prüfen, ob die Maybe Monade den drei Monadengesetzen gehorcht.
- Das linke Identitätsgesetz -
return x >>= f = fx
return z >>= f
= (Just z) >>= f
= f z
- Das richtige Identitätsgesetz -
m >>= return = m
-
JustDatenkonstruktor
Just z >>= return
= return z
= Just z
-
NothingDatenkonstruktor
Nothing >>= return
= Nothing
- Das Assoziativitätsgesetz -
(m >>= f) >>= g = m >>= (\x -> fx >>= g)
-
JustDatenkonstruktor
-- Left-hand side
((Just z) >>= f) >>= g
= f z >>= g
-- Right-hand side
(Just z) >>= (\x -> f x >>= g)
(\x -> f x >>= g) z
= f z >>= g
-
NothingDatenkonstruktor
-- Left-hand side
(Nothing >>= f) >>= g
= Nothing >>= g
= Nothing
-- Right-hand side
Nothing >>= (\x -> f x >>= g)
= Nothing
IO Monade
Es gibt keine Möglichkeit, einen Wert des Typs a aus einem Ausdruck des Typs IO a und dies sollte nicht der Fall sein. Dies ist eigentlich ein großer Teil des Grunds, warum Monaden zum Modellieren von IO .
Ein Ausdruck des Typs IO a kann als eine Aktion bezeichnet werden, die mit der realen Welt interagieren kann und, wenn sie ausgeführt wird, zu etwas vom Typ a . Zum Beispiel bedeutet die Funktion getLine :: IO String aus dem Vorspiel nicht, dass sich unter getLine eine bestimmte Zeichenfolge befindet, die ich extrahieren kann. Dies bedeutet, dass getLine die Aktion darstellt, bei der eine Zeile aus der Standardeingabe abgerufen wird.
Es überrascht nicht, dass main :: IO () da ein Haskell-Programm eine Berechnung / Aktion darstellt, die mit der realen Welt interagiert.
Die Dinge können Sie Ausdrücke vom Typ tun IO a , weil IO Monade ist:
Folgen Sie zwei Aktionen mit
(>>), um eine neue Aktion zu erzeugen, die die erste Aktion ausführt, den von ihr erzeugten Wert verwirft und anschließend die zweite Aktion ausführt.-- print the lines "Hello" then "World" to stdout putStrLn "Hello" >> putStrLn "World"Manchmal möchten Sie den Wert, der in der ersten Aktion erzeugt wurde, nicht verwerfen. Sie möchten, dass er tatsächlich in eine zweite Aktion eingespeist wird. Dafür haben wir
>>=. FürIOhat es den Typ(>>=) :: IO a -> (a -> IO b) -> IO b.-- get a line from stdin and print it back out getLine >>= putStrLnNehmen Sie einen normalen Wert und konvertieren Sie ihn in eine Aktion, die den von Ihnen angegebenen Wert sofort zurückgibt. Diese Funktion ist weniger offensichtlich nützlich , bis Sie beginnen
doNotation.-- make an action that just returns 5 return 5
Mehr aus dem Haskell-Wiki zur IO-Monade hier .
Liste Monade
Die Listen bilden eine Monade. Sie haben eine monadische Instanziierung, die dieser entspricht:
instance Monad [] where return x = [x] xs >>= f = concat (map f xs)
Wir können sie verwenden, um Nicht-Determinismus in unseren Berechnungen nachzuahmen. Wenn wir xs >>= f , wird die Funktion f :: a -> [b] über die Liste xs abgebildet, wobei eine Liste mit Ergebnislisten jeder Anwendung von f über jedes Element von xs und alle Listen von erhalten wird Die Ergebnisse werden dann in einer Liste aller Ergebnisse zusammengefasst. Als Beispiel berechnen wir eine Summe aus zwei nicht-deterministischen Zahlen unter Verwendung der Schreibweise , wobei die Summe durch eine Liste von Summen aller Paare von Ganzzahlen aus zwei Listen dargestellt wird, wobei jede Liste alle möglichen Werte einer nicht-deterministischen Zahl darstellt:
sumnd xs ys = do
x <- xs
y <- ys
return (x + y)
Oder gleichwertig mit liftM2 in Control.Monad :
sumnd = liftM2 (+)
wir erhalten:
> sumnd [1,2,3] [0,10]
[1,11,2,12,3,13]
Monade als Unterklasse von Applicative
Ab GHC 7.10 ist Applicative eine Oberklasse von Monad (dh jeder Typ, der eine Monad muss auch ein Applicative ). Alle Methoden von Applicative ( pure , <*> ) können als Methoden von Monad ( return , >>= ) implementiert werden.
Es ist offensichtlich, dass pure und return gleichwertige Zwecke haben, also pure = return . Die Definition für <*> ist zu relativ klar:
mf <*> mx = do { f <- mf; x <- mx; return (f x) }
-- = mf >>= (\f -> mx >>= (\x -> return (f x)))
-- = [r | f <- mf, x <- mx, r <- return (f x)] -- with MonadComprehensions
-- = [f x | f <- mf, x <- mx]
Diese Funktion ist in den Standardbibliotheken als ap definiert.
Wenn Sie also bereits eine Instanz von Monad für einen Typ definiert haben, können Sie eine Instanz von Applicative für ihn "kostenlos" erhalten, indem Sie sie definieren
instance Applicative < type > where
pure = return
(<*>) = ap
Wie bei den Monadengesetzen werden diese Gleichwertigkeiten nicht durchgesetzt, aber die Entwickler sollten sicherstellen, dass sie immer eingehalten werden.
Kein allgemeiner Weg, um Wert aus einer monadischen Berechnung zu ziehen
Sie können Werte in Aktionen umschließen und das Ergebnis einer Berechnung in eine andere umleiten:
return :: Monad m => a -> m a
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Die Definition einer Monade garantiert jedoch nicht die Existenz einer Funktion des Typs Monad m => ma -> a .
Das bedeutet, dass es im Allgemeinen keine Möglichkeit gibt, einen Wert aus einer Berechnung zu extrahieren (dh ihn "auspacken"). Dies ist in vielen Fällen der Fall:
extract :: Maybe a -> a
extract (Just x) = x -- Sure, this works, but...
extract Nothing = undefined -- We can’t extract a value from failure.
Insbesondere gibt es keine Funktion IO a -> a , die Anfänger oft verwirrt; siehe dieses Beispiel .
Notation
do notation ist syntaktischer Zucker für Monaden. Hier sind die Regeln:
do x <- mx do x <- mx y <- my is equivalent to do y <- my ... ...
do let a = b let a = b in ... is equivalent to do ...
do m m >> ( e is equivalent to e)
do x <- m m >>= (\x -> e is equivalent to e)
do m is equivalent to m
Zum Beispiel sind diese Definitionen gleichwertig:
example :: IO Integer
example =
putStrLn "What's your name?" >> (
getLine >>= (\name ->
putStrLn ("Hello, " ++ name ++ ".") >> (
putStrLn "What should we return?" >> (
getLine >>= (\line ->
let n = (read line :: Integer) in
return (n + n))))))
example :: IO Integer
example = do
putStrLn "What's your name?"
name <- getLine
putStrLn ("Hello, " ++ name ++ ".")
putStrLn "What should we return?"
line <- getLine
let n = (read line :: Integer)
return (n + n)
Definition von Monade
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
Die wichtigste Funktion im Umgang mit Monaden ist der Bind-Operator >>= :
(>>=) :: m a -> (a -> m b) -> m b
- Stellen Sie sich
maals "eine Aktion mitaErgebnis" vor . - Stellen Sie sich
a -> mbals "eine Aktion (abhängig vonaParameter) mit einembErgebnis" vor .
>>= sequenziert zwei Aktionen zusammen, indem das Ergebnis von der ersten Aktion zur zweiten weitergeleitet wird.
Die andere von Monad definierte Funktion ist:
return :: a -> m a
Der Name ist unglücklich: Diese return hat nichts mit dem in imperativen Programmiersprachen gefundenen return Schlüsselwort zu tun.
return x ist die triviale Aktion, die als Ergebnis x ergibt. (Es ist im folgenden Sinne trivial :)
return x >>= f ≡ f x -- “left identity” monad law
x >>= return ≡ x -- “right identity” monad law