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 }
  • takeOneEighth ist eine Abfolge von drei halve Schritten, die miteinander verkettet sind.
  • Wenn ein halve fehlschlägt, möchten wir, dass die gesamte Komposition takeOneEighth fehlschlägt.
  • Wenn eine halve Stufe 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.

  1. Das linke Identitätsgesetz - return x >>= f = fx
return z >>= f 
= (Just z) >>= f 
= f z
  1. Das richtige Identitätsgesetz - m >>= return = m
  • Just Datenkonstruktor
Just z >>= return
= return z
= Just z  
  • Nothing Datenkonstruktor
Nothing >>= return
= Nothing 
  1. Das Assoziativitätsgesetz - (m >>= f) >>= g = m >>= (\x -> fx >>= g)
  • Just Datenkonstruktor
-- 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
  • Nothing Datenkonstruktor
-- 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ür IO hat es den Typ (>>=) :: IO a -> (a -> IO b) -> IO b .

     -- get a line from stdin and print it back out
     getLine >>= putStrLn
    
  • Nehmen 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 do Notation.

     -- 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 ma als "eine Aktion mit a Ergebnis" vor .
  • Stellen Sie sich a -> mb als "eine Aktion (abhängig von a Parameter) mit einem b Ergebnis" 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


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow