haskell - functores - que es una monada programacion




¿Qué es una mónada? (20)

tl; dr

{-# LANGUAGE InstanceSigs #-}

newtype Id t = Id t

instance Monad Id where
   return :: t -> Id t
   return = Id

   (=<<) :: (a -> Id b) -> Id a -> Id b
   f =<< (Id x) = f x

Prólogo

El operador $de la aplicación de funciones

forall a b. a -> b

se define canónicamente

($) :: (a -> b) -> a -> b
f $ x = f x

infixr 0 $

en términos de aplicación de función primitiva Haskell fx( infixl 10).

La composición .se define en términos de $como

(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \ x -> f $ g x

infixr 9 .

y satisface las equivalencias forall fg h.

     f . id  =  f            :: c -> d   Right identity
     id . g  =  g            :: b -> c   Left identity
(f . g) . h  =  f . (g . h)  :: a -> d   Associativity

.es asociativo, y ides su identidad derecha e izquierda.

El triple de Kleisli

En programación, una mónada es un constructor de tipo functor con una instancia de la clase de tipo mónada. Hay varias variantes equivalentes de definición e implementación, cada una con intuiciones ligeramente diferentes sobre la abstracción de la mónada.

Un functor es un constructor fde tipo de tipo * -> *con una instancia de la clase de tipo functor.

{-# LANGUAGE KindSignatures #-}

class Functor (f :: * -> *) where
   map :: (a -> b) -> (f a -> f b)

Además de seguir el protocolo de tipo forzado estáticamente, las instancias de la clase de tipo functor deben obedecer las leyes de funge algebraicas forall f g.

       map id  =  id           :: f t -> f t   Identity
map f . map g  =  map (f . g)  :: f a -> f c   Composition / short cut fusion

Los cálculos de functor tienen el tipo

forall f t. Functor f => f t

Un cálculo crconsiste en resultados r dentro del contexto c .

Las funciones monádicas unarias o las flechas de Kleisli tienen el tipo

forall m a b. Functor m => a -> m b

Las flechas de Kleisi son funciones que toman un argumento ay devuelven un cálculo monádico mb.

Las mónadas se definen canónicamente en términos del triple de Kleisli forall m. Functor m =>

(m, return, (=<<))

implementado como la clase de tipo

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

infixr 1 =<<

La identidad de Kleisli return es una flecha de Kleisli que promueve un valor ten un contexto monádico m. La aplicación de extensión o Kleisli=<< aplica una flecha de Kleisli a -> mba los resultados de un cálculo ma.

La composición de Kleisli <=< se define en términos de extensión como

(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = \ x -> f =<< g x

infixr 1 <=<

<=< compone dos flechas Kleisli, aplicando la flecha izquierda a los resultados de la aplicación de la flecha derecha.

Las instancias de la clase de tipo mónada deben obedecer las leyes de mónada , más elegantemente expresadas en términos de composición de Kleisli:forall fg h.

   f <=< return  =  f                :: c -> m d   Right identity
   return <=< g  =  g                :: b -> m c   Left identity
(f <=< g) <=< h  =  f <=< (g <=< h)  :: a -> m d   Associativity

<=<es asociativo, y returnes su identidad derecha e izquierda.

Identidad

El tipo de identidad

type Id t = t

es la función de identidad en tipos

Id :: * -> *

Interpretado como un functor,

   return :: t -> Id t
=      id :: t ->    t

    (=<<) :: (a -> Id b) -> Id a -> Id b
=     ($) :: (a ->    b) ->    a ->    b

    (<=<) :: (b -> Id c) -> (a -> Id b) -> (a -> Id c)
=     (.) :: (b ->    c) -> (a ->    b) -> (a ->    c)

En Haskell canónico, se define la mónada de identidad

newtype Id t = Id t

instance Functor Id where
   map :: (a -> b) -> Id a -> Id b
   map f (Id x) = Id (f x)

instance Monad Id where
   return :: t -> Id t
   return = Id

   (=<<) :: (a -> Id b) -> Id a -> Id b
   f =<< (Id x) = f x

Opción

Un tipo de opción

data Maybe t = Nothing | Just t

codifica la computación Maybe tque no necesariamente produce un resultado t, computación que puede "fallar". La opción mónada está definida

instance Functor Maybe where
   map :: (a -> b) -> (Maybe a -> Maybe b)
   map f (Just x) = Just (f x)
   map _ Nothing  = Nothing

instance Monad Maybe where
   return :: t -> Maybe t
   return = Just

   (=<<) :: (a -> Maybe b) -> Maybe a -> Maybe b
   f =<< (Just x) = f x
   _ =<< Nothing  = Nothing

a -> Maybe bse aplica a un resultado solo si Maybe aproduce un resultado.

newtype Nat = Nat Int

Los números naturales pueden codificarse como aquellos enteros mayores o iguales a cero.

toNat :: Int -> Maybe Nat
toNat i | i >= 0    = Just (Nat i)
        | otherwise = Nothing

Los números naturales no se cierran bajo resta.

(-?) :: Nat -> Nat -> Maybe Nat
(Nat n) -? (Nat m) = toNat (n - m)

infixl 6 -?

La opción mónada cubre una forma básica de manejo de excepciones.

(-? 20) <=< toNat :: Int -> Maybe Nat

Lista

La mónada de la lista, sobre el tipo de lista

data [] t = [] | t : [t]

infixr 5 :

y su operación monoide aditiva "agregar"

(++) :: [t] -> [t] -> [t]
(x : xs) ++ ys = x : xs ++ ys
[]       ++ ys = ys

infixr 5 ++

codifica el cálculo no lineal que[t] produce una cantidad natural 0, 1, ...de resultados t.

instance Functor [] where
   map :: (a -> b) -> ([a] -> [b])
   map f (x : xs) = f x : map f xs
   map _ []       = []

instance Monad [] where
   return :: t -> [t]
   return = (: [])

   (=<<) :: (a -> [b]) -> [a] -> [b]
   f =<< (x : xs) = f x ++ (f =<< xs)
   _ =<< []       = []

La extensión =<<concatena ++todas las listas [b]resultantes de las aplicaciones fxde una flecha de Kleisli a -> [b]a elementos de [a]una sola lista de resultados [b].

Deje que los divisores propios de un entero positivo nsean

divisors :: Integral t => t -> [t]
divisors n = filter (`divides` n) [2 .. n - 1]

divides :: Integral t => t -> t -> Bool
(`divides` n) = (== 0) . (n `rem`)

luego

forall n.  let { f = f <=< divisors } in f n   =   []

Al definir la clase de tipo mónada, en lugar de extensión =<<, el estándar Haskell usa su operador flip, el enlace>>= .

class Applicative m => Monad m where
   (>>=) :: forall a b. m a -> (a -> m b) -> m b

   (>>) :: forall a b. m a -> m b -> m b
   m >> k = m >>= \ _ -> k
   {-# INLINE (>>) #-}

   return :: a -> m a
   return = pure

Por simplicidad, esta explicación usa la jerarquía de clases de tipos

class              Functor f
class Functor m => Monad m

En Haskell, la jerarquía estándar actual es

class                  Functor f
class Functor p     => Applicative p
class Applicative m => Monad m

porque no solo cada mónada es functor, sino que cada aplicativo es un ficticio y cada mónada también es un aplicativo.

Usando la lista mónada, el pseudocódigo imperativo

for a in (1, ..., 10)
   for b in (1, ..., 10)
      p <- a * b
      if even(p)
         yield p

se traduce aproximadamente al bloque do ,

do a <- [1 .. 10]
   b <- [1 .. 10]
   let p = a * b
   guard (even p)
   return p

la comprensión de mónada equivalente ,

[ p | a <- [1 .. 10], b <- [1 .. 10], let p = a * b, even p ]

y la expresión

[1 .. 10] >>= (\ a ->
   [1 .. 10] >>= (\ b ->
      let p = a * b in
         guard (even p) >>       -- [ () | even p ] >>
            return p
      )
   )

La comprensión de notación y mónada son azúcar sintáctica para expresiones de enlace anidadas. El operador de enlace se usa para el enlace de nombre local de resultados monádicos.

let x = v in e    =   (\ x -> e)  $  v   =   v  &  (\ x -> e)
do { r <- m; c }  =   (\ r -> c) =<< m   =   m >>= (\ r -> c)

dónde

(&) :: a -> (a -> b) -> b
(&) = flip ($)

infixl 0 &

La función de guardia está definida

guard :: Additive m => Bool -> m ()
guard True  = return ()
guard False = fail

donde el tipo de unidad o "tupla vacía"

data () = ()

Las mónadas aditivas que admiten elección y falla pueden abstraerse utilizando una clase de tipo

class Monad m => Additive m where
   fail  :: m t
   (<|>) :: m t -> m t -> m t

infixl 3 <|>

instance Additive Maybe where
   fail = Nothing

   Nothing <|> m = m
   m       <|> _ = m

instance Additive [] where
   fail = []
   (<|>) = (++)

donde faily <|>formar un monoideforall kl m.

     k <|> fail  =  k
     fail <|> l  =  l
(k <|> l) <|> m  =  k <|> (l <|> m)

y failes el elemento cero absorbente / aniquilador de mónadas aditivas

_ =<< fail  =  fail

Si en

guard (even p) >> return p

even pes cierto, entonces el guardia produce [()]y, por definición de >>, la función constante local

\ _ -> return p

se aplica al resultado (). Si es falso, el guardia produce la lista mónada fail( []), que no produce ningún resultado para que se aplique una flecha de Kleisli >>, por lo que pse omite.

Estado

Infamemente, las mónadas se utilizan para codificar la computación con estado.

Un procesador de estado es una función.

forall st t. st -> (t, st)

que transiciona un estado sty produce un resultado t. El estado st puede ser cualquier cosa. Nada, bandera, conteo, matriz, mango, máquina, mundo.

El tipo de procesadores de estado generalmente se llama

type State st t = st -> (t, st)

La mónada del procesador de estado es el * -> *functor amable State st. Las flechas de Kleisli de la mónada del procesador de estado son funciones

forall st a b. a -> (State st) b

En Haskell canónico, se define la versión perezosa de la mónada del procesador de estado

newtype State st t = State { stateProc :: st -> (t, st) }

instance Functor (State st) where
   map :: (a -> b) -> ((State st) a -> (State st) b)
   map f (State p) = State $ \ s0 -> let (x, s1) = p s0
                                     in  (f x, s1)

instance Monad (State st) where
   return :: t -> (State st) t
   return x = State $ \ s -> (x, s)

   (=<<) :: (a -> (State st) b) -> (State st) a -> (State st) b
   f =<< (State p) = State $ \ s0 -> let (x, s1) = p s0
                                     in  stateProc (f x) s1

Un procesador de estado se ejecuta proporcionando un estado inicial:

run :: State st t -> st -> (t, st)
run = stateProc

eval :: State st t -> st -> t
eval = fst . run

exec :: State st t -> st -> st
exec = snd . run

El acceso estatal es proporcionado por primitivas gety putmétodos de abstracción sobre mónadas con estado :

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class Monad m => Stateful m st m -> st where
   get :: m st
   put :: st -> m ()

m -> stdeclara una dependencia funcional del tipo de estado sten la mónada m; que a State t, por ejemplo, determinará que el tipo de estado sea texclusivo.

instance Stateful (State st) st where
   get :: State st st
   get = State $ \ s -> (s, s)

   put :: st -> State st ()
   put s = State $ \ _ -> ((), s)

con el tipo de unidad utilizado de forma análoga a voiden C.

modify :: Stateful m st => (st -> st) -> m ()
modify f = do
   s <- get
   put (f s)

gets :: Stateful m st => (st -> t) -> m t
gets f = do
   s <- get
   return (f s)

gets a menudo se usa con los accesos de campo de registro.

El estado de la mónada equivalente del subproceso variable

let s0 = 34
    s1 = (+ 1) s0
    n = (* 12) s1
    s2 = (+ 7) s1
in  (show n, s2)

donde s0 :: Int, es igualmente transparente referencialmente, pero infinitamente más elegante y práctico

(flip run) 34
   (do
      modify (+ 1)
      n <- gets (* 12)
      modify (+ 7)
      return (show n)
   )

modify (+ 1)es un cálculo de tipo State Int (), excepto por su efecto equivalente a return ().

(flip run) 34
   (modify (+ 1) >>
      gets (* 12) >>= (\ n ->
         modify (+ 7) >>
            return (show n)
      )
   )

La ley de asociatividad de la mónada se puede escribir en términos de >>= forall mf g.

(m >>= f) >>= g  =  m >>= (\ x -> f x >>= g)

o

do {                 do {                   do {
   r1 <- do {           x <- m;                r0 <- m;
      r0 <- m;   =      do {            =      r1 <- f r0;
      f r0                 r1 <- f x;          g r1
   };                      g r1             }
   g r1                 }
}                    }

Al igual que en la programación orientada a la expresión (por ejemplo, Rust), la última declaración de un bloque representa su rendimiento. El operador de enlace a veces se denomina "punto y coma programable".

Las primitivas de la estructura de control de iteración de la programación imperativa estructurada se emulan monádicamente.

for :: Monad m => (a -> m b) -> [a] -> m ()
for f = foldr ((>>) . f) (return ())

while :: Monad m => m Bool -> m t -> m ()
while c m = do
   b <- c
   if b then m >> while c m
        else return ()

forever :: Monad m => m t
forever m = m >> forever m

De entrada y salida

data World

La mónada del procesador de estado mundial de E / S es una reconciliación de Haskell puro y el mundo real, de la semántica operativa imperativa y denotativa funcional. Un análogo cercano de la implementación estricta real:

type IO t = World -> (t, World)

La interacción es facilitada por primitivas impuras

getChar         :: IO Char
putChar         :: Char -> IO ()
readFile        :: FilePath -> IO String
writeFile       :: FilePath -> String -> IO ()
hSetBuffering   :: Handle -> BufferMode -> IO ()
hTell           :: Handle -> IO Integer
. . .              . . .

La impureza del código que usa IOprimitivas está permanentemente protocolizada por el sistema de tipos. Debido a que la pureza es asombrosa, lo que sucede en IO, se queda adentro IO.

unsafePerformIO :: IO t -> t

O, al menos, debería.

La firma tipo de un programa Haskell

main :: IO ()
main = putStrLn "Hello, World!"

se expande a

World -> ((), World)

Una función que transforma un mundo.

Epílogo

La categoría cuyos objetos son tipos de Haskell y cuyos morfismos son funciones entre los tipos de Haskell es, "rápido y suelto", la categoría Hask.

Un functor Tes un mapeo de una categoría Ca una categoría D; para cada objeto en Cun objeto enD

Tobj :  Obj(C) -> Obj(D)
   f :: *      -> *

y para cada morfismo en Cun morfismo enD

Tmor :  HomC(X, Y) -> HomD(Tobj(X), Tobj(Y))
 map :: (a -> b)   -> (f a -> f b)

donde X, Yson objetos en C. HomC(X, Y)es la clase de homomorfismo de todos los morfismos X -> Yen C. El functor debe preservar la identidad y composición del morfismo, la "estructura" de C, en D.

                    Tmor    Tobj

      T(id)  =  id        : T(X) -> T(X)   Identity
T(f) . T(g)  =  T(f . g)  : T(X) -> T(Z)   Composition

La categoría Kleisli de una categoría Cestá dada por un triple Kleisli

<T, eta, _*>

de un endofunctor

T : C -> C

( f), un morfismo de identidad eta( return) y un operador de extensión *( =<<).

Cada morfismo de Kleisli en Hask

      f :  X -> T(Y)
      f :: a -> m b

por el operador de extensión

   (_)* :  Hom(X, T(Y)) -> Hom(T(X), T(Y))
  (=<<) :: (a -> m b)   -> (m a -> m b)

se le da un morfismo en Haskla categoría de Kleisli

     f* :  T(X) -> T(Y)
(f =<<) :: m a  -> m b

La composición en la categoría Kleisli .Tse da en términos de extensión

 f .T g  =  f* . g       :  X -> T(Z)
f <=< g  =  (f =<<) . g  :: a -> m c

y satisface los axiomas de categoría

       eta .T g  =  g                :  Y -> T(Z)   Left identity
   return <=< g  =  g                :: b -> m c

       f .T eta  =  f                :  Z -> T(U)   Right identity
   f <=< return  =  f                :: c -> m d

  (f .T g) .T h  =  f .T (g .T h)    :  X -> T(U)   Associativity
(f <=< g) <=< h  =  f <=< (g <=< h)  :: a -> m d

que, aplicando las transformaciones de equivalencia

     eta .T g  =  g
     eta* . g  =  g               By definition of .T
     eta* . g  =  id . g          forall f.  id . f  =  f
         eta*  =  id              forall f g h.  f . h  =  g . h  ==>  f  =  g

(f .T g) .T h  =  f .T (g .T h)
(f* . g)* . h  =  f* . (g* . h)   By definition of .T
(f* . g)* . h  =  f* . g* . h     . is associative
    (f* . g)*  =  f* . g*         forall f g h.  f . h  =  g . h  ==>  f  =  g

en términos de extensión se dan canónicamente

               eta*  =  id                 :  T(X) -> T(X)   Left identity
       (return =<<)  =  id                 :: m t -> m t

           f* . eta  =  f                  :  Z -> T(U)      Right identity
   (f =<<) . return  =  f                  :: c -> m d

          (f* . g)*  =  f* . g*            :  T(X) -> T(Z)   Associativity
(((f =<<) . g) =<<)  =  (f =<<) . (g =<<)  :: m a -> m c

Las mónadas también se pueden definir en términos no de extensión de Kleislian, sino de una transformación natural mu, en la programación llamada join. Una mónada se define en términos de muun triple sobre una categoría C, de un endofunctor

     T :  C -> C
     f :: * -> *

y dos transformaciones naturales

   eta :  Id -> T
return :: t  -> f t

    mu :  T . T   -> T
  join :: f (f t) -> f t

satisfaciendo las equivalencias

       mu . T(mu)  =  mu . mu               :  T . T . T -> T . T   Associativity
  join . map join  =  join . join           :: f (f (f t)) -> f t

      mu . T(eta)  =  mu . eta       =  id  :  T -> T               Identity
join . map return  =  join . return  =  id  :: f t -> f t

La clase de tipo mónada se define entonces

class Functor m => Monad m where
   return :: t -> m t
   join   :: m (m t) -> m t

La muimplementación canónica de la opción mónada:

instance Monad Maybe where
   return = Just

   join (Just m) = m
   join Nothing  = Nothing

La concatfunción

concat :: [[a]] -> [a]
concat (x : xs) = x ++ concat xs
concat []       = []

es el joinde la lista mónada.

instance Monad [] where
   return :: t -> [t]
   return = (: [])

   (=<<) :: (a -> [b]) -> ([a] -> [b])
   (f =<<) = concat . map f

Las implementaciones de joinse pueden traducir del formulario de extensión utilizando la equivalencia

     mu  =  id*           :  T . T -> T
   join  =  (id =<<)      :: m (m t) -> m t

La traducción inversa de mua forma de extensión viene dada por

     f*  =  mu . T(f)     :  T(X) -> T(Y)
(f =<<)  =  join . map f  :: m a -> m b

Pero, ¿por qué una teoría tan abstracta podría ser útil para la programación?

La respuesta es simple: como informáticos, ¡ valoramos la abstracción ! Cuando diseñamos la interfaz para un componente de software, queremos que revele lo menos posible sobre la implementación. Queremos poder reemplazar la implementación con muchas alternativas, muchas otras 'instancias' del mismo 'concepto'. Cuando diseñamos una interfaz genérica para muchas bibliotecas de programas, es aún más importante que la interfaz que elijamos tenga una variedad de implementaciones. Es la generalidad del concepto de mónada lo que valoramos tanto, porque la teoría de categorías es tan abstracta que sus conceptos son tan útiles para la programación.

No es sorprendente, entonces, que la generalización de las mónadas que presentamos a continuación también tenga una estrecha conexión con la teoría de categorías. Pero hacemos hincapié en que nuestro propósito es muy práctico: no es "implementar la teoría de categorías", es encontrar una forma más general de estructurar las bibliotecas de combinador. ¡Es simplemente nuestra buena suerte que los matemáticos ya hayan hecho gran parte del trabajo por nosotros!

de generalizar mónadas a flechas por John Hughes

Después de haber examinado brevemente a Haskell recientemente, ¿cuál sería una explicación breve, sucinta y práctica de lo que esencialmente es una mónada?

La mayoría de las explicaciones que he encontrado son bastante inaccesibles y carecen de detalles prácticos.


Después de mucho esfuerzo, creo que finalmente entiendo la mónada. Después de releer mi larga crítica de la respuesta abrumadoramente votada, ofreceré esta explicación.

Hay tres preguntas que deben responderse para comprender las mónadas:

  1. ¿Por qué necesitas una mónada?
  2. ¿Qué es una mónada?
  3. ¿Cómo se implementa una mónada?

Como señalé en mis comentarios originales, demasiadas explicaciones de mónada quedan atrapadas en la pregunta número 3, sin, y antes de realmente cubrir adecuadamente la pregunta 2 o la pregunta 1.

¿Por qué necesitas una mónada?

Los lenguajes funcionales puros como Haskell son diferentes de los lenguajes imperativos como C o Java en que un programa funcional puro no se ejecuta necesariamente en un orden específico, un paso a la vez. Un programa Haskell es más parecido a una función matemática, en la que puede resolver la "ecuación" en cualquier número de órdenes potenciales. Esto confiere una serie de beneficios, entre los que se encuentra que elimina la posibilidad de ciertos tipos de errores, particularmente aquellos relacionados con cosas como "estado".

Sin embargo, hay ciertos problemas que no son tan fáciles de resolver con este estilo de programación. Algunas cosas, como la programación de la consola y la E / S de archivos, necesitan que las cosas sucedan en un orden particular, o necesitan mantener el estado. Una forma de abordar este problema es crear un tipo de objeto que represente el estado de un cálculo y una serie de funciones que toman un objeto de estado como entrada y devuelven un nuevo objeto de estado modificado.

Entonces, creemos un valor hipotético de "estado", que represente el estado de una pantalla de consola.exactamente cómo se construye este valor no es importante, pero digamos que es una matriz de caracteres ASCII de longitud de bytes que representa lo que está visible actualmente en la pantalla, y una matriz que representa la última línea de entrada ingresada por el usuario, en pseudocódigo. Hemos definido algunas funciones que toman el estado de la consola, la modifican y devuelven un nuevo estado de la consola.

consolestate MyConsole = new consolestate;

Entonces, para hacer la programación de la consola, pero de una manera puramente funcional, necesitaría anidar muchas llamadas de función dentro de cada una.

consolestate FinalConsole = print(input(print(myconsole, "Hello, what's your name?")),"hello, %inputbuffer%!");

La programación de esta manera mantiene el estilo funcional "puro", al tiempo que obliga a que se realicen cambios en la consola en un orden particular. Pero probablemente querremos hacer más que unas pocas operaciones a la vez, como en el ejemplo anterior. Las funciones de anidamiento de esa manera comenzarán a ser torpes. Lo que queremos es un código que haga esencialmente lo mismo que el anterior, pero que esté escrito un poco más así:

consolestate FinalConsole = myconsole:
                            print("Hello, what's your name?"):
                            input():
                            print("hello, %inputbuffer%!");

De hecho, esta sería una forma más conveniente de escribirlo. ¿Cómo hacemos eso sin embargo?

¿Qué es una mónada?

Una vez que tenga un tipo (como consolestate) que defina junto con un conjunto de funciones diseñadas específicamente para operar en ese tipo, puede convertir todo el paquete de estas cosas en una "mónada" definiendo un operador como :(vincular) que automáticamente alimenta los valores de retorno a su izquierda, en los parámetros de función a su derecha, y un liftoperador que convierte las funciones normales, en funciones que funcionan con ese tipo específico de operador de enlace.

¿Cómo se implementa una mónada?

Vea otras respuestas, que parecen bastante libres para entrar en detalles.


Explicar "¿Qué es una mónada?" Es un poco como decir "¿Qué es un número?" Usamos números todo el tiempo. Pero imagina que conociste a alguien que no sabía nada sobre números. ¿Cómo diablos explicarías qué son los números? ¿Y cómo comenzarías a describir por qué eso podría ser útil?

¿Qué es una mónada? La respuesta corta: es una forma específica de encadenar operaciones juntas.

En esencia, está escribiendo pasos de ejecución y vinculándolos con la "función de enlace". (En Haskell, se llama >>= .) Puede escribir las llamadas al operador de enlace usted mismo, o puede usar la sintaxis sugar que hace que el compilador inserte esas llamadas de función por usted. Pero de cualquier manera, cada paso está separado por una llamada a esta función de enlace.

Entonces la función de enlace es como un punto y coma; Separa los pasos en un proceso. El trabajo de la función de vinculación es tomar la salida del paso anterior y pasarla al siguiente paso.

Eso no suena demasiado difícil, ¿verdad? Pero hay más de un tipo de mónada. ¿Por qué? ¿Cómo?

Bueno, la función de vinculación solo puede tomar el resultado de un paso y pasarlo al siguiente. Pero si eso es "todo" que hace la mónada ... eso en realidad no es muy útil. Y eso es importante de entender: cada mónada útil hace algo más además de ser una mónada. Cada mónada útil tiene un "poder especial", que lo hace único.

(Una mónada que no hace nada especial se llama "mónada de identidad". Más bien como la función de identidad, esto suena como algo completamente inútil, pero resulta que no es ... Pero esa es otra historia ™).

Básicamente, cada mónada tiene su propia implementación de la función de vinculación. Y puede escribir una función de enlace de modo que haga cosas entre los pasos de ejecución. Por ejemplo:

  • Si cada paso devuelve un indicador de éxito / fracaso, puede hacer que Bind ejecute el siguiente paso solo si el anterior tuvo éxito. De esta manera, un paso fallido anula la secuencia completa "automáticamente", sin ninguna prueba condicional de su parte. (La falla Mónada .)

  • Extendiendo esta idea, puede implementar "excepciones". (La mónada de error o la mónada de excepción ). Debido a que los está definiendo usted mismo en lugar de ser una característica del lenguaje, puede definir cómo funcionan. (Por ejemplo, tal vez quieras ignorar las dos primeras excepciones y solo abortar cuando se lanza una tercera excepción).

  • Puede hacer que cada paso devuelva múltiples resultados , y hacer que la función de enlace se repita sobre ellos, alimentando cada uno en el siguiente paso para usted. De esta manera, no tiene que seguir escribiendo bucles por todas partes cuando se trata de múltiples resultados. La función de enlace "automáticamente" hace todo eso por usted. (La Lista Mónada .)

  • Además de pasar un "resultado" de un paso a otro, también puede hacer que la función de enlace pase datos adicionales . Estos datos ahora no aparecen en su código fuente, pero aún puede acceder a ellos desde cualquier lugar, sin tener que pasarlos manualmente a todas las funciones. (El lector Monad .)

  • Puede hacerlo para que se puedan reemplazar los "datos adicionales". Esto le permite simular actualizaciones destructivas , sin hacer actualizaciones destructivas. (La mónada estatal y su primo el escritor mónada ).

  • Debido a que solo está simulando actualizaciones destructivas, puede hacer cosas triviales que serían imposibles con actualizaciones destructivas reales . Por ejemplo, puede deshacer la última actualización o volver a una versión anterior .

  • Puede hacer una mónada donde los cálculos se pueden pausar , por lo que puede pausar su programa, entrar y jugar con los datos de estado internos, y luego reanudarlo.

  • Puede implementar "continuaciones" como una mónada. ¡Esto te permite romper las mentes de las personas!

Todo esto y más es posible con las mónadas. Por supuesto, todo esto también es perfectamente posible sin mónadas. Es drásticamente más fácil usar mónadas.


Pero, ¡podrías haber inventado mónadas!

sigfpe dice:

Pero todo esto introduce a las mónadas como algo esotérico que necesita explicación. Pero lo que quiero argumentar es que no son esotéricos en absoluto. De hecho, ante varios problemas en la programación funcional, habría sido llevado, inexorablemente, a ciertas soluciones, todas las cuales son ejemplos de mónadas. De hecho, espero que los inventes ahora si aún no lo has hecho. Entonces es un pequeño paso notar que todas estas soluciones son, de hecho, la misma solución disfrazada. Y después de leer esto, es posible que esté en una mejor posición para comprender otros documentos sobre mónadas porque reconocerá todo lo que ve como algo que ya ha inventado.

Muchos de los problemas que las mónadas intentan resolver están relacionados con el tema de los efectos secundarios. Entonces comenzaremos con ellos. (Tenga en cuenta que las mónadas le permiten hacer más que manejar los efectos secundarios, en particular muchos tipos de objeto contenedor pueden verse como mónadas. Algunas de las introducciones a las mónadas tienen dificultades para conciliar estos dos usos diferentes de las mónadas y concentrarse en solo uno o el otro.)

En un lenguaje de programación imperativo como C ++, las funciones no se comportan como las funciones de las matemáticas. Por ejemplo, supongamos que tenemos una función C ++ que toma un único argumento de coma flotante y devuelve un resultado de coma flotante. Superficialmente, puede parecer un poco como una función matemática que asigna reales a reales, pero una función C ++ puede hacer más que devolver un número que depende de sus argumentos. Puede leer y escribir los valores de las variables globales, así como escribir la salida en la pantalla y recibir la entrada del usuario. Sin embargo, en un lenguaje funcional puro, una función solo puede leer lo que se le proporciona en sus argumentos y la única forma en que puede tener un efecto en el mundo es a través de los valores que devuelve.


Primero: el término mónada es un poco vacío si no eres matemático. Un término alternativo es generador de cómputo, que es un poco más descriptivo de para qué son realmente útiles.

Pides ejemplos prácticos:

Ejemplo 1: comprensión de la lista :

[x*2 | x<-[1..10], odd x]

Esta expresión devuelve los dobles de todos los números impares en el rango de 1 a 10. ¡Muy útil!

Resulta que esto es solo azúcar sintáctico para algunas operaciones dentro de la mónada List. La misma lista de comprensión se puede escribir como:

do
   x <- [1..10]
   guard (odd x)
   return (x * 2)

O incluso:

[1..10] >>= (\x -> guard (odd x) >> return (x*2))

Ejemplo 2: Entrada / Salida :

do
   putStrLn "What is your name?"
   name <- getLine
   putStrLn ("Welcome, " ++ name ++ "!")

Ambos ejemplos usan mónadas, constructores de cálculo AKA. El tema común es que la mónada encadena las operaciones de alguna manera específica y útil. En la comprensión de la lista, las operaciones se encadenan de tal manera que si una operación devuelve una lista, las siguientes operaciones se realizan en cada elemento de la lista. Por otro lado, la mónada IO realiza las operaciones secuencialmente, pero pasa una "variable oculta", que representa "el estado del mundo", que nos permite escribir código de E / S de una manera puramente funcional.

Resulta que el patrón de operaciones de encadenamiento es bastante útil y se utiliza para muchas cosas diferentes en Haskell.

Otro ejemplo son las excepciones: al usar la mónada Error , las operaciones se encadenan de tal manera que se realizan de forma secuencial, excepto si se produce un error, en cuyo caso se abandona el resto de la cadena.

Tanto la sintaxis de comprensión de la lista como la notación do son azúcar sintáctica para encadenar operaciones utilizando el operador >>= . Una mónada es básicamente un tipo que admite el operador >>= .

Ejemplo 3: un analizador sintáctico

Este es un analizador muy simple que analiza una cadena entre comillas o un número:

parseExpr = parseString <|> parseNumber

parseString = do
        char '"'
        x <- many (noneOf "\"")
        char '"'
        return (StringValue x)

parseNumber = do
    num <- many1 digit
    return (NumberValue (read num))

Las operaciones char , digit , etc. son bastante simples. O coinciden o no coinciden. La magia es la mónada que gestiona el flujo de control: las operaciones se realizan de forma secuencial hasta que falla una coincidencia, en cuyo caso la mónada retrocede al último <|> e intenta la siguiente opción. Una vez más, una forma de encadenar operaciones con algunas semánticas adicionales útiles.

Ejemplo 4: programación asincrónica

Los ejemplos anteriores están en Haskell, pero resulta que F# también admite mónadas. Este ejemplo es robado de Don Syme :

let AsyncHttp(url:string) =
    async {  let req = WebRequest.Create(url)
             let! rsp = req.GetResponseAsync()
             use stream = rsp.GetResponseStream()
             use reader = new System.IO.StreamReader(stream)
             return reader.ReadToEnd() }

Este método busca una página web. La línea de perforación es el uso de GetResponseAsync : en realidad espera la respuesta en un hilo separado, mientras que el hilo principal regresa de la función. Las últimas tres líneas se ejecutan en el hilo generado cuando se ha recibido la respuesta.

En la mayoría de los otros idiomas, tendría que crear explícitamente una función separada para las líneas que manejan la respuesta. La mónada async es capaz de "dividir" el bloque por sí solo y posponer la ejecución de la segunda mitad. (La sintaxis async {} indica que el flujo de control en el bloque está definido por la mónada async ).

Cómo trabajan ellos

Entonces, ¿cómo puede una mónada hacer todas estas cosas elegantes de flujo de control? Lo que realmente sucede en un do-block (o una expresión de cálculo como se los llama en F #), es que cada operación (básicamente cada línea) está envuelta en una función anónima separada. Estas funciones se combinan utilizando el operador de bind (escrito >>= en Haskell). Dado que la operación de bind combina funciones, puede ejecutarlas como mejor le parezca: secuencialmente, varias veces, a la inversa, descarte algunas, ejecute algunas en un hilo separado cuando lo desee y así sucesivamente.

Como ejemplo, esta es la versión ampliada del código IO del ejemplo 2:

putStrLn "What is your name?"
>>= (\_ -> getLine)
>>= (\name -> putStrLn ("Welcome, " ++ name ++ "!"))

Esto es más feo, pero también es más obvio lo que realmente está sucediendo. El operador >>= es el ingrediente mágico: toma un valor (en el lado izquierdo) y lo combina con una función (en el lado derecho) para producir un nuevo valor. Este nuevo valor es tomado por el siguiente operador >>= y nuevamente combinado con una función para producir un nuevo valor. >>= se puede ver como un mini evaluador.

Tenga en cuenta que >>= está sobrecargado para diferentes tipos, por lo que cada mónada tiene su propia implementación de >>= . (Sin embargo, todas las operaciones en la cadena deben ser del tipo de la misma mónada; de lo contrario, el operador >>= no funcionará).

La implementación más simple posible de >>= simplemente toma el valor a la izquierda y lo aplica a la función a la derecha y devuelve el resultado, pero como se dijo antes, lo que hace que todo el patrón sea útil es cuando hay algo extra en el implementación de mónada de >>= .

Existe cierta inteligencia adicional en cómo se pasan los valores de una operación a la siguiente, pero esto requiere una explicación más profunda del sistema de tipo Haskell.

Resumiendo

En términos de Haskell, una mónada es un tipo parametrizado que es una instancia de la clase de tipo Mónada, que define >>= junto con algunos otros operadores. En términos simples, una mónada es solo un tipo para el que se define la operación >>= .

En sí >>= es solo una forma engorrosa de encadenar funciones, pero con la presencia de la notación que oculta la "fontanería", las operaciones monádicas resultan ser una abstracción muy agradable y útil, útil en muchos lugares del lenguaje. y útil para crear sus propios mini idiomas en el idioma.

¿Por qué son duras las mónadas?

Para muchos estudiantes de Haskell, las mónadas son un obstáculo que golpean como una pared de ladrillos. No es que las mónadas en sí mismas sean complejas, sino que la implementación se basa en muchas otras características avanzadas de Haskell como tipos parametrizados, clases de tipos, etc. El problema es que Haskell I / O se basa en mónadas, y I / O es probablemente una de las primeras cosas que quieres entender cuando aprendes un nuevo idioma; después de todo, no es muy divertido crear programas que no produzcan salida. No tengo una solución inmediata para este problema de huevo y gallina, excepto tratar la E / S como "la magia sucede aquí" hasta que tenga suficiente experiencia con otras partes del lenguaje. Lo siento.

Excelente blog sobre mónadas: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html


Una mónada es un tipo de datos que tiene dos operaciones: >>= (también conocido como bind ) y return (también conocido como unit ). return toma un valor arbitrario y crea una instancia de la mónada con él. >>= toma una instancia de la mónada y asigna una función sobre ella. (Ya puede ver que una mónada es un tipo de datos extraño, ya que en la mayoría de los lenguajes de programación no se puede escribir una función que tome un valor arbitrario y cree un tipo a partir de ella. Las mónadas usan un tipo de polimorfismo paramétrico ).

En notación Haskell, la interfaz de mónada está escrita

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

Se supone que estas operaciones deben obedecer ciertas "leyes", pero eso no es extremadamente importante: las "leyes" simplemente codifican la forma en que las implementaciones sensatas de las operaciones deberían comportarse (básicamente, que >>= y return deberían estar de acuerdo sobre cómo se transforman los valores en instancias de mónada y que >>= es asociativo).

Las mónadas no son solo sobre estado y E / S: resumen un patrón común de cómputo que incluye trabajar con estado, E / S, excepciones y no determinismo. Probablemente las mónadas más simples de entender son las listas y los tipos de opciones:

instance Monad [ ] where
    []     >>= k = []
    (x:xs) >>= k = k x ++ (xs >>= k)
    return x     = [x]

instance Monad Maybe where
    Just x  >>= k = k x
    Nothing >>= k = Nothing
    return x      = Just x

donde [] y : son los constructores de la lista, ++ es el operador de concatenación, y Just and Nothing son los constructores de Maybe . Ambas mónadas encapsulan patrones de cálculo comunes y útiles en sus respectivos tipos de datos (tenga en cuenta que ninguno tiene nada que ver con los efectos secundarios o E / S).

Realmente tienes que jugar escribiendo un código Haskell no trivial para apreciar de qué se tratan las mónadas y por qué son útiles.




Después de responder a esta pregunta hace unos años, creo que puedo mejorar y simplificar esa respuesta con ...

Una mónada es una técnica de composición de funciones que externaliza el tratamiento para algunos escenarios de entrada utilizando una función de composición bind, para preprocesar la entrada durante la composición.

En composición normal, la función, compose (>>)se usa para aplicar la función compuesta al resultado de su predecesor en secuencia. Es importante destacar que la función que se compone es necesaria para manejar todos los escenarios de su entrada.

(x -> y) >> (y -> z)

Este diseño se puede mejorar reestructurando la entrada para que los estados relevantes se interroguen más fácilmente. Entonces, en lugar de simplemente yel valor puede convertirse Mben, por ejemplo, (is_OK, b)si se yincluye una noción de validez.

Por ejemplo, cuando la entrada es solamente posiblemente un número, en lugar de devolver una cadena que puede contener diligentemente contener un número o no, usted podría reestructurar el tipo en un boolindicando la presencia de un número válido y un número en tupla tal como, bool * float. Las funciones compuestas ya no necesitarían analizar una cadena de entrada para determinar si existe un número, sino que simplemente podrían inspeccionar la boolparte de una tupla.

(Ma -> Mb) >> (Mb -> Mc)

Aquí, una vez más, la composición ocurre naturalmente con, composepor lo que cada función debe manejar todos los escenarios de su entrada individualmente, aunque hacerlo ahora es mucho más fácil.

Sin embargo, ¿qué pasaría si pudiéramos externalizar el esfuerzo de interrogación para aquellos momentos en los que manejar un escenario es una rutina? Por ejemplo, ¿qué pasa si nuestro programa no hace nada cuando la entrada no está bien como en cuando lo is_OKestá false? Si eso se hiciera, las funciones compuestas no tendrían que manejar ese escenario por sí mismas, simplificando drásticamente su código y logrando otro nivel de reutilización.

Para lograr esta externalización podríamos usar una función bind (>>=),, para realizar el en compositionlugar de compose. Como tal, en lugar de simplemente transferir valores de la salida de una función a la entrada de otra Bind, inspeccionaría la Mporción May decidiría si aplicar la función compuesta a la función compuesta y cómo a. Por supuesto, la función bindse definiría específicamente para nuestro particular a Mfin de poder inspeccionar su estructura y realizar cualquier tipo de aplicación que queramos. No obstante, apuede ser cualquier cosa, ya que bindsimplemente pasa lo no ainspeccionado a la función compuesta cuando determina la aplicación necesaria. Además, las funciones compuestas en sí mismas ya no necesitan lidiar conMparte de la estructura de entrada tampoco, simplificándolas. Por lo tanto...

(a -> Mb) >>= (b -> Mc) o más sucintamente Mb >>= (b -> Mc)

En resumen, una mónada se externaliza y, por lo tanto, proporciona un comportamiento estándar en torno al tratamiento de ciertos escenarios de entrada una vez que la entrada se diseña para exponerlos suficientemente. Este diseño es un shell and contentmodelo en el que el shell contiene datos relevantes para la aplicación de la función compuesta y es interrogado y solo está disponible para la bindfunción.

Por lo tanto, una mónada es tres cosas:

  1. un Mcaparazón para contener información relevante de mónada
  2. una bindfunción implementada para hacer uso de esta información de shell en su aplicación de las funciones compuestas a los valores de contenido que encuentra dentro del shell, y
  3. funciones componibles de la forma, a -> Mbproduciendo resultados que incluyen datos de gestión monádica.

En términos generales, la entrada a una función es mucho más restrictiva que su salida, que puede incluir cosas como condiciones de error; por lo tanto, la Mbestructura de resultados es generalmente muy útil. Por ejemplo, el operador de división no devuelve un número cuando el divisor es 0.

Además, monads puede incluir funciones de ajuste que envuelven valores, aen el tipo monádico Ma, y funciones generales a -> b, en funciones monádicas a -> Mb, ajustando sus resultados después de la aplicación. Por supuesto, como bind, tales funciones de ajuste son específicas de M. Un ejemplo:

let return a = [a]
let lift f a = return (f a)

El diseño de la bindfunción supone estructuras de datos inmutables y funciones puras, otras cosas se vuelven complejas y no se pueden hacer garantías. Como tal, hay leyes monádicas:

Dado...

M_ 
return = (a -> Ma)
f = (a -> Mb)
g = (b -> Mc)

Luego...

Left Identity  : (return a) >>= f === f a
Right Identity : Ma >>= return    === Ma
Associative    : Ma >>= (f >>= g) === Ma >>= ((fun x -> f x) >>= g)

Associativitysignifica que bindconserva el orden de evaluación independientemente de cuándo bindse aplique. Es decir, en la definición de Associativityarriba, la evaluación temprana forzada del paréntesis bindingde fy gsolo dará como resultado una función que se espera Mapara completar el bind. Por lo tanto, la evaluación de Madebe determinarse antes de que su valor pueda aplicarse fy ese resultado a su vez se aplique g.


En la práctica, monad es una implementación personalizada del operador de composición de funciones que se encarga de los efectos secundarios y los valores de entrada y retorno incompatibles (para el encadenamiento).


Las dos cosas que me ayudaron mejor al aprender allí fueron:

Capítulo 8, "Analizadores funcionales", del libro de Graham Hutton Programming in Haskell . En realidad, esto no menciona a las mónadas, pero si puede trabajar en el capítulo y comprender realmente todo lo que contiene, particularmente cómo se evalúa una secuencia de operaciones de enlace, comprenderá las partes internas de las mónadas. Espere que esto tome varios intentos.

El tutorial Todo sobre las mónadas . Esto da varios buenos ejemplos de su uso, y debo decir que la analogía en Appendex funcionó para mí.


Las mónadas deben controlar el flujo de los tipos de datos abstractos para los datos.

En otras palabras, muchos desarrolladores se sienten cómodos con la idea de conjuntos, listas, diccionarios (o hashes o mapas) y árboles. Dentro de esos tipos de datos hay muchos casos especiales (por ejemplo, InsertionOrderPreservingIdentityHashMap).

Sin embargo, cuando se enfrentan con el "flujo" del programa, muchos desarrolladores no han estado expuestos a muchas más construcciones que if, switch / case, do, while, goto (grr) y (quizás) cierres.

Entonces, una mónada es simplemente una construcción de flujo de control. Una mejor frase para reemplazar a la mónada sería 'tipo de control'.

Como tal, una mónada tiene ranuras para la lógica de control, o declaraciones o funciones: el equivalente en las estructuras de datos sería decir que algunas estructuras de datos le permiten agregar datos y eliminarlos.

Por ejemplo, la mónada "if":

if( clause ) then block

en su forma más simple tiene dos espacios: una cláusula y un bloque. La ifmónada generalmente se construye para evaluar el resultado de la cláusula y, si no es falso, evalúa el bloque. Muchos desarrolladores no son introducidos a las mónadas cuando aprenden 'si', y simplemente no es necesario comprender a las mónadas para escribir una lógica efectiva.

Las mónadas pueden volverse más complicadas, de la misma manera que las estructuras de datos pueden volverse más complicadas, pero hay muchas categorías amplias de mónadas que pueden tener una semántica similar, pero diferentes implementaciones y sintaxis.

Por supuesto, de la misma manera que las estructuras de datos pueden iterarse o atravesarse, las mónadas pueden evaluarse.

Los compiladores pueden o no tener soporte para mónadas definidas por el usuario. Haskell ciertamente lo hace. Ioke tiene algunas capacidades similares, aunque el término mónada no se usa en el lenguaje.


Mi tutorial favorito de Monad:

http://www.haskell.org/haskellwiki/All_About_Monads

(¡de 170,000 visitas en una búsqueda en Google de "mónada tutorial"!)

@Stu: El objetivo de las mónadas es permitirle agregar (generalmente) semántica secuencial a un código puro; incluso puede componer mónadas (usando Transformadores de mónada) y obtener una semántica combinada más interesante y complicada, como el análisis con manejo de errores, estado compartido y registro, por ejemplo. Todo esto es posible en código puro, las mónadas solo le permiten abstraerlo y reutilizarlo en bibliotecas modulares (siempre bueno en programación), así como proporcionar una sintaxis conveniente para que parezca imprescindible.

Haskell ya tiene una sobrecarga de operadores [1]: utiliza clases de tipos de la misma manera que uno podría usar interfaces en Java o C #, pero resulta que Haskell también permite tokens no alfanuméricos como + && y> como identificadores de infijo. Es solo sobrecarga del operador en su forma de verlo si quiere decir "sobrecargar el punto y coma" [2]. Suena como magia negra y pide problemas para "sobrecargar el punto y coma" (los piratas informáticos emprendedores de Perl se enteran de esta idea) pero el punto es que sin mónadas no hay punto y coma, ya que el código puramente funcional no requiere ni permite una secuencia explícita.

Todo esto suena mucho más complicado de lo necesario. El artículo de sigfpe es bastante bueno, pero utiliza a Haskell para explicarlo, lo que no logra resolver el problema del huevo y la gallina de entender a Haskell para mimar a las mónadas y entender a las mónadas para que las mimeticen.

[1] Este es un problema separado de las mónadas, pero las mónadas utilizan la función de sobrecarga del operador de Haskell.

[2] Esto también es una simplificación excesiva ya que el operador para encadenar acciones monádicas es >> = (pronunciado "bind") pero hay un azúcar sintáctico ("do") que le permite usar llaves y puntos y comas y / o sangría y líneas nuevas.


Además de las excelentes respuestas anteriores, permítame ofrecerle un enlace al siguiente artículo (de Patrick Thomson) que explica las mónadas relacionando el concepto con la biblioteca JavaScript jQuery (y su forma de usar el "encadenamiento de métodos" para manipular el DOM) : jQuery es una mónada

La documentación de jQuery en sí no se refiere al término "mónada", sino que habla sobre el "patrón de construcción", que probablemente sea más familiar. Esto no cambia el hecho de que tienes una mónada adecuada allí quizás sin siquiera darte cuenta.


En el contexto de Scala, encontrará que la siguiente es la definición más simple. Básicamente flatMap (o bind) es 'asociativo' y existe una identidad.

trait M[+A] {
  def flatMap[B](f: A => M[B]): M[B] // AKA bind

  // Pseudo Meta Code
  def isValidMonad: Boolean = {
    // for every parameter the following holds
    def isAssociativeOn[X, Y, Z](x: M[X], f: X => M[Y], g: Y => M[Z]): Boolean =
      x.flatMap(f).flatMap(g) == x.flatMap(f(_).flatMap(g))

    // for every parameter X and x, there exists an id
    // such that the following holds
    def isAnIdentity[X](x: M[X], id: X => M[X]): Boolean =
      x.flatMap(id) == x
  }
}

P.ej

// These could be any functions
val f: Int => Option[String] = number => if (number == 7) Some("hello") else None
val g: String => Option[Double] = string => Some(3.14)

// Observe these are identical. Since Option is a Monad 
// they will always be identical no matter what the functions are
scala> Some(7).flatMap(f).flatMap(g)
res211: Option[Double] = Some(3.14)

scala> Some(7).flatMap(f(_).flatMap(g))
res212: Option[Double] = Some(3.14)


// As Option is a Monad, there exists an identity:
val id: Int => Option[Int] = x => Some(x)

// Observe these are identical
scala> Some(7).flatMap(id)
res213: Option[Int] = Some(7)

scala> Some(7)
res214: Some[Int] = Some(7)

NOTA Estrictamente hablando, la definición de una mónada en la programación funcional no es la misma que la definición de una mónada en la teoría de categorías , que se define en turnos de mapy flatten. Aunque son una especie de equivalente bajo ciertas asignaciones. Esta presentación es muy buena: http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-category


He estado pensando en las mónadas de una manera diferente, últimamente. He estado pensando en ellos como abstrayendo el orden de ejecución de una manera matemática, lo que hace posible nuevos tipos de polimorfismo.

Si está utilizando un lenguaje imperativo y escribe algunas expresiones en orden, el código SIEMPRE se ejecuta exactamente en ese orden.

Y en el caso simple, cuando usa una mónada, se siente igual: define una lista de expresiones que suceden en orden. Excepto que, dependiendo de la mónada que use, su código podría ejecutarse en orden (como en la mónada IO), en paralelo sobre varios elementos a la vez (como en la mónada Lista), podría detenerse a la mitad (como en la mónada Quizás) , podría detenerse a medio camino para reanudarse más tarde (como en una mónada Reanudación), podría rebobinarse y comenzar desde el principio (como en una mónada de Transacción), o podría rebobinarse parcialmente para probar otras opciones (como en una mónada Lógica) .

Y debido a que las mónadas son polimórficas, es posible ejecutar el mismo código en diferentes mónadas, según sus necesidades.

Además, en algunos casos, es posible combinar mónadas (con transformadores de mónada) para obtener múltiples funciones al mismo tiempo.



Todavía soy nuevo en las mónadas, pero pensé que compartiría un enlace que encontré que se sintió realmente bien para leer (¡CON FOTOS!): http://www.matusiak.eu/numerodix/blog/2012/3/11/monads-for-the-layman/ (sin afiliación)

Básicamente, el concepto cálido y difuso que obtuve del artículo fue el concepto de que las mónadas son básicamente adaptadores que permiten que funciones dispares funcionen de una manera composable, es decir, ser capaz de agrupar múltiples funciones y mezclarlas y combinarlas sin preocuparse por un retorno inconsistente tipos y tal. Entonces, la función BIND se encarga de mantener las manzanas con manzanas y las naranjas con naranjas cuando intentamos hacer estos adaptadores. Y la función LIFT se encarga de tomar las funciones de "nivel inferior" y "actualizarlas" para que funcionen con las funciones BIND y también sean componibles.

Espero haberlo entendido bien y, lo que es más importante, espero que el artículo tenga una visión válida sobre las mónadas. Por lo menos, este artículo me ayudó a despertar mi apetito por aprender más sobre las mónadas.


Una mónada es una cosa utilizada para encapsular objetos que tienen un estado cambiante. Se encuentra con mayor frecuencia en idiomas que, de lo contrario, no le permiten tener un estado modificable (por ejemplo, Haskell).

Un ejemplo sería para el archivo de E / S.

Podrías usar una mónada para E / S de archivo para aislar la naturaleza de estado cambiante solo al código que usó la mónada. El código dentro de la mónada puede ignorar efectivamente el estado cambiante del mundo fuera de la mónada; esto hace que sea mucho más fácil razonar sobre el efecto general de su programa.


Una mónada es una forma de combinar cálculos que comparten un contexto común. Es como construir una red de tuberías. Al construir la red, no fluyen datos a través de ella. Pero cuando termine de unir todos los bits con 'bind' y 'return', invoco algo así runMyMonad monad datay los datos fluyen a través de las tuberías.





terminology