Buscar..


Introducción

Una mónada es un tipo de datos de acciones compuestas. Monad es la clase de constructores de tipo cuyos valores representan tales acciones. Tal vez IO es el más reconocible uno: un valor de IO a es una "receta para recuperar un a valor en el mundo real".

Decimos que un constructor de tipo m (como [] o Maybe ) forma una mónada si hay una instance Monad m cumple ciertas leyes sobre la composición de las acciones. Entonces podemos razonar sobre ma como una "acción cuyo resultado tiene el tipo a ".

La mónada Tal vez

Maybe se utiliza para representar valores posiblemente vacíos, similar a null en otros idiomas. Normalmente se usa como el tipo de salida de funciones que pueden fallar de alguna manera.

Considera la siguiente función:

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

Piense en la halve como una acción, dependiendo de un Int , que intenta reducir a la mitad el número entero, fallando si es impar.

¿Cómo halve un número entero tres veces?

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 es una secuencia de tres pasos de la halve encadenados.
  • Si un paso de la halve falla, queremos que la composición completa takeOneEighth falle.
  • Si un paso de la halve tiene éxito, queremos canalizar su resultado hacia adelante.

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

y ahora podemos escribir:

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 composición de Kleisli <=< se define como (g <=< f) x = g =<< fx , o equivalentemente como (f >=> g) x = fx >>= g . Con ello la definición anterior se convierte en justa.

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

Hay tres leyes de la mónada que deben ser obedecidas por cada mónada, es decir, cada tipo que es una instancia de la clase de tipos de la Monad :

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

donde m es una mónada, f tiene tipo a -> mb y g tiene tipo b -> mc .

O de manera equivalente, utilizando el operador de composición Kleisli >=> definido anteriormente:

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

Obedecer estas leyes hace que sea mucho más fácil razonar acerca de la mónada, porque garantiza que el uso de funciones monádicas y su composición se comporte de una manera razonable, similar a otras mónadas.

Vamos a comprobar si la mónada Maybe obedece las tres leyes de la mónada.

  1. La ley de identidad de la izquierda - return x >>= f = fx
return z >>= f 
= (Just z) >>= f 
= f z
  1. La ley de identidad correcta - m >>= return = m
  • Just constructor de datos
Just z >>= return
= return z
= Just z  
  • Constructor de Nothing datos
Nothing >>= return
= Nothing 
  1. La ley de asociatividad - (m >>= f) >>= g = m >>= (\x -> fx >>= g)
  • Just constructor de datos
-- 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
  • Constructor de Nothing datos
-- Left-hand side
(Nothing >>= f) >>= g
= Nothing >>= g
= Nothing

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

IO mónada

No hay forma de obtener un valor de tipo a fuera de una expresión de tipo IO a y no debería haber. Esto es en realidad una gran parte de por qué las mónadas se utilizan para modelar IO .

Se puede considerar que una expresión de tipo IO a representa una acción que puede interactuar con el mundo real y, si se ejecuta, daría lugar a algo de tipo a . Por ejemplo, la función getLine :: IO String del preludio no significa que debajo de getLine haya una cadena específica que pueda extraer; significa que getLine representa la acción de obtener una línea desde la entrada estándar.

No es sorprendente que main :: IO () ya que un programa Haskell representa una computación / acción que interactúa con el mundo real.

Lo que puede hacer con expresiones de tipo IO a porque IO es una mónada:

  • Secuencia dos acciones usando (>>) para producir una nueva acción que ejecuta la primera acción, descarta cualquier valor que produzca y luego ejecuta la segunda acción.

      -- print the lines "Hello" then "World" to stdout
      putStrLn "Hello" >> putStrLn "World"
    
  • A veces no desea descartar el valor que se produjo en la primera acción; en realidad, le gustaría que se introdujera en una segunda acción. Para eso, tenemos >>= . Para IO , tiene tipo (>>=) :: IO a -> (a -> IO b) -> IO b .

     -- get a line from stdin and print it back out
     getLine >>= putStrLn
    
  • Tome un valor normal y conviértalo en una acción que inmediatamente devuelva el valor que le dio. Esta función es menos útil, obviamente, hasta que empiece a usar notación do

     -- make an action that just returns 5
     return 5
    

Más de la Wiki de Haskell en la mónada IO aquí .

Lista Mónada

Las listas forman una mónada. Tienen una instanciación de mónada equivalente a esta:

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

Podemos usarlos para emular el no determinismo en nuestros cálculos. Cuando usamos xs >>= f , la función f :: a -> [b] se mapea sobre la lista xs , obteniendo una lista de listas de resultados de cada aplicación de f sobre cada elemento de xs , y todas las listas de los resultados se concatenan en una lista de todos los resultados. Como ejemplo, calculamos una suma de dos números no deterministas utilizando do-notation , representada por la lista de sumas de todos los pares de enteros de dos listas, cada una de las cuales representa todos los valores posibles de un número no determinista:

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

O equivalentemente, usando liftM2 en Control.Monad :

sumnd = liftM2 (+)

obtenemos:

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

La mónada como subclase de aplicativo

A partir de GHC 7.10, el Applicative es una superclase de la Monad (es decir, todo tipo que sea una Monad también debe ser un Applicative ). Todos los métodos de Applicative ( pure , <*> ) se pueden implementar en términos de métodos de Monad ( return , >>= ).

Es obvio que los propósitos pure y de return son equivalentes, tan pure = return . La definición de <*> es demasiado clara:

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]                   

Esta función se define como ap en las bibliotecas estándar.

Por lo tanto, si ya ha definido una instancia de Monad para un tipo, efectivamente puede obtener una instancia de Applicative para ella "gratis" definiendo

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

Al igual que con las leyes de la mónada, estas equivalencias no se aplican, pero los desarrolladores deben asegurarse de que siempre se respeten.

No hay forma general de extraer valor de un cálculo monádico

Puede ajustar valores en acciones y canalizar el resultado de un cálculo en otro:

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

Sin embargo, la definición de una Mónada no garantiza la existencia de una función de tipo Monad m => ma -> a .

Eso significa que, en general, no hay forma de extraer un valor de un cálculo (es decir, "desenvolverlo"). Este es el caso de muchos casos:

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

Específicamente, no hay ninguna función IO a -> a , que a menudo confunde a los principiantes; ver este ejemplo

hacer notación

do anotación es azúcar sintáctica para las mónadas. Estas son las reglas:

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

Por ejemplo, estas definiciones son equivalentes:

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)

Definición de mónada

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

La función más importante para tratar con las mónadas es el operador de enlace >>= :

(>>=) :: m a -> (a -> m b) -> m b
  • Piense en ma como "una acción con un a resultado".
  • Piense en a -> mb “una acción (en función de una como a parámetro) con una b . Consecuencia”.

>>= secuencia dos acciones juntas canalizando el resultado de la primera acción a la segunda.

La otra función definida por Monad es:

return :: a -> m a

Su nombre es desafortunado: este return no tiene nada que ver con la palabra clave de return encontrada en lenguajes de programación imperativos.

return x es la acción trivial que produce x como su resultado. (Es trivial en el siguiente sentido :)

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow