Ricerca…


introduzione

Una monade è un tipo di dati di azioni componibili. Monad è la classe di costruttori di tipi i cui valori rappresentano tali azioni. Forse IO è il più riconoscibile uno: un valore di IO a è una "ricetta per il recupero di un a valore dal mondo reale".

Diciamo che un costruttore di tipo m (come [] o Maybe ) forma una monade se esiste instance Monad m soddisfa determinate leggi sulla composizione delle azioni. Possiamo quindi ragionare su ma come "azione il cui risultato ha tipo a ".

La forse monade

Maybe è usato per rappresentare valori probabilmente vuoti - simile a null in altre lingue. Di solito è usato come tipo di output di funzioni che possono fallire in qualche modo.

Considera la seguente funzione:

halve :: Int -> Maybe Int
halve x
  | even x = Just (x `div` 2)
  | odd x  = Nothing

Pensa a halve come un'azione, a seconda di un Int , che prova a dimezzare il numero intero, in caso contrario se è strano.

Come halve un intero tre volte?

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 è una sequenza di tre passaggi halve concatenati.
  • Se un passo di halve fallisce, vogliamo che l'intera composizione takeOneEighth per fallire.
  • Se un passo della halve riesce, vogliamo invertire il suo risultato.

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

e ora possiamo scrivere:

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

La composizione di Kleisli <=< è definita come (g <=< f) x = g =<< fx , o equivalentemente come (f >=> g) x = fx >>= g . Con esso la definizione di cui sopra diventa giusta

takeOneEighth :: Int -> Maybe Int
takeOneEighth = halve <=< halve <=< halve               -- infixr 1 <=<
        -- or, equivalently,                    
        --      halve >=> halve >=> halve               -- infixr 1 >=>    

Ci sono tre leggi monad che dovrebbero essere osservate da ogni monade, cioè ogni tipo che è un'istanza della classe di Monad :

1.  return x >>= f  =  f x
2.    m >>= return  =  m
3. (m >>= g) >>= h  =  m >>= (\y -> g y >>= h)

dove m è una monade, f ha tipo a -> mb e g ha tipo b -> mc .

O in modo equivalente, usando l'operatore di composizione >=> Kleisli sopra definito:

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 } }

Obbedire a queste leggi rende molto più facile ragionare sulla monade, perché garantisce che l'uso di funzioni monadiche e la loro composizione si comporti in modo ragionevole, simile ad altre monadi.

Controlliamo se la Maybe monade obbedisce alle tre leggi monad.

  1. La legge di identità sinistra - return x >>= f = fx
return z >>= f 
= (Just z) >>= f 
= f z
  1. La giusta legge sull'identità - m >>= return = m
  • Just costruttore di dati
Just z >>= return
= return z
= Just z  
  • Nothing costruttore di dati
Nothing >>= return
= Nothing 
  1. La legge di associatività - (m >>= f) >>= g = m >>= (\x -> fx >>= g)
  • Just costruttore di dati
-- 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 costruttore di dati
-- Left-hand side
(Nothing >>= f) >>= g
= Nothing >>= g
= Nothing

-- Right-hand side
Nothing >>= (\x -> f x >>= g)
= Nothing

IO monad

Non c'è modo di ottenere un valore di tipo a da un'espressione di tipo IO a e non dovrebbe esserci. Questa è in realtà una grande parte del motivo per cui le monadi sono utilizzate per modellare IO .

Un'espressione di tipo IO a può essere pensata come rappresentativa di un'azione che può interagire con il mondo reale e, se eseguita, risulterebbe in qualcosa di tipo a . Ad esempio, la funzione getLine :: IO String dal preludio non significa che al di sotto di getLine ci sia una stringa specifica che posso estrarre, significa che getLine rappresenta l'azione di ottenere una linea dallo standard input.

Non sorprendentemente, main :: IO () poiché un programma Haskell rappresenta un calcolo / azione che interagisce con il mondo reale.

Le cose che puoi fare alle espressioni di tipo IO a perché IO è una monade:

  • Sequenza di due azioni utilizzando (>>) per produrre una nuova azione che esegue la prima azione, elimina qualsiasi valore prodotto e quindi esegue la seconda azione.

      -- print the lines "Hello" then "World" to stdout
      putStrLn "Hello" >> putStrLn "World"
    
  • A volte non vuoi scartare il valore che è stato prodotto nella prima azione: in realtà ti piacerebbe che venisse inserito in una seconda azione. Per questo, abbiamo >>= . Per IO , ha type (>>=) :: IO a -> (a -> IO b) -> IO b .

     -- get a line from stdin and print it back out
     getLine >>= putStrLn
    
  • Prendi un valore normale e convertilo in un'azione che restituisce immediatamente il valore che gli hai dato. Questa funzione è meno ovviamente utile fino a quando iniziare a usare do la notazione.

     -- make an action that just returns 5
     return 5
    

Altro dal Wiki Haskell sulla monade IO qui .

Lista Monad

Le liste formano una monade. Hanno una istanziazione monad equivalente a questo:

instance Monad [] where 
  return x = [x]
  xs >>= f = concat (map f xs)               

Possiamo usarli per emulare il non-determinismo nei nostri calcoli. Quando usiamo xs >>= f , la funzione f :: a -> [b] è mappata sulla lista xs , ottenendo un elenco di liste di risultati di ciascuna applicazione di f su ciascun elemento di xs e tutti gli elenchi di i risultati vengono quindi concatenati in una lista di tutti i risultati. Ad esempio, calcoliamo una somma di due numeri non deterministici usando la notazione , la somma rappresentata dall'elenco di somme di tutte le coppie di numeri interi da due elenchi, ciascuna lista che rappresenta tutti i possibili valori di un numero non deterministico:

sumnd xs ys = do
  x <- xs
  y <- ys
  return (x + y)

O in modo equivalente, utilizzando liftM2 in Control.Monad :

sumnd = liftM2 (+)

otteniamo:

> sumnd [1,2,3] [0,10]
[1,11,2,12,3,13]

Monad come sottoclasse di Applicative

A partire da GHC 7.10, Applicative è una superclasse di Monad (cioè, ogni tipo che è una Monad deve essere anche un Applicative ). Tutti i metodi di Applicative ( pure , <*> ) possono essere implementati in termini di metodi di Monad ( return , >>= ).

È ovvio che il pure e il return servono scopi equivalenti, quindi pure = return . La definizione di <*> è troppo chiara:

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]                   

Questa funzione è definita come ap nelle librerie standard.

Quindi se hai già definito un'istanza di Monad per un tipo, puoi effettivamente ottenere un'istanza di Applicative per questo "gratuitamente" definendo

instance Applicative < type > where
    pure  = return
    (<*>) = ap

Come con le leggi monadi, queste equivalenze non sono applicate, ma gli sviluppatori dovrebbero assicurarsi di essere sempre rispettate.

Nessun modo generale per estrarre il valore da un calcolo monadico

Puoi racchiudere i valori in azioni e convogliare il risultato di un calcolo in un altro:

return :: Monad m => a -> m a
(>>=)  :: Monad m => m a -> (a -> m b) -> m b

Tuttavia, la definizione di una Monade non garantisce l'esistenza di una funzione di tipo Monad m => ma -> a .

Ciò significa che non c'è, in generale, alcun modo per estrarre un valore da un calcolo (cioè "scartarlo"). Questo è il caso per molte istanze:

extract :: Maybe a -> a
extract (Just x) = x          -- Sure, this works, but...
extract Nothing  = undefined  -- We can’t extract a value from failure.

Nello specifico, non vi è alcuna funzione IO a -> a , che spesso confonde i principianti; guarda questo esempio

fare notazione

do notazione è zucchero sintattico per le monadi. Ecco le regole:

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

Ad esempio, queste definizioni sono equivalenti:

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)

Definizione di Monad

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

La funzione più importante per gestire le monadi è l' operatore di bind >>= :

(>>=) :: m a -> (a -> m b) -> m b
  • Pensare ma come "un azione con un a risultato".
  • Pensa a a -> mb come "un'azione (a seconda di a parametro) con un risultato b " .

>>= sequenzia due azioni insieme piping il risultato dalla prima azione al secondo.

L'altra funzione definita da Monad è:

return :: a -> m a

Il suo nome è sfortunato: questo return non ha nulla a che fare con la parola chiave return trovata nei linguaggi di programmazione imperativi.

return x è l'azione banale che produce x come risultato. (È banale nel senso seguente :)

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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow