una que programacion monada monad functores fing dividir haskell functional-programming monads terminology

haskell - que - ¿Qué es una mónada?



monada maybe (30)

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.


En realidad, contrario a la comprensión común de las mónadas, no tienen nada que ver con el estado. Las mónadas son simplemente una forma de envolver cosas y proporcionar métodos para realizar operaciones en las cosas envueltas sin desenvolverlas.

Por ejemplo, puede crear un tipo para envolver otro, en Haskell:

data Wrapped a = Wrap a

Para envolver cosas definimos

return :: a -> Wrapped a return x = Wrap x

Para realizar operaciones sin desenvolver, digamos que tiene una función f :: a -> b , luego puede hacer esto para levantar esa función para actuar sobre valores envueltos:

fmap :: (a -> b) -> (Wrapped a -> Wrapped b) fmap f (Wrap x) = Wrap (f x)

Eso es todo lo que hay que entender. Sin embargo, resulta que hay una función más general para hacer este levantamiento , que es bind :

bind :: (a -> Wrapped b) -> (Wrapped a -> Wrapped b) bind f (Wrap x) = f x

bind puede hacer un poco más que fmap , pero no al revés. En realidad, fmap se puede definir en términos de bind y return . Entonces, al definir una mónada ... le das su tipo (aquí estaba Wrapped a ) y luego dices cómo funcionan sus operaciones de return y bind .

Lo bueno es que esto resulta ser un patrón tan general que aparece por todas partes, el estado de encapsulación de manera pura es solo uno de ellos.

Para un buen artículo sobre cómo las mónadas se pueden usar para introducir dependencias funcionales y así controlar el orden de evaluación, como se usa en la mónada IO de Haskell, consulte IO Inside .

En cuanto a la comprensión de las mónadas, no te preocupes demasiado por eso. Lea sobre ellos lo que le parezca interesante y no se preocupe si no entiende de inmediato. Entonces simplemente bucear en un idioma como Haskell es el camino a seguir. Las mónadas son una de estas cosas en las que la comprensión llega a tu cerebro mediante la práctica, un día te das cuenta de repente de que las entiendes.


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 debes entender qué es un functor. Antes de eso, comprenda las funciones de orden superior.

Una función de orden superior es simplemente una función que toma una función como argumento.

Un functor es cualquier construcción de tipo T para la cual existe una función de orden superior, llamada map , que transforma una función de tipo a -> b (dados dos tipos a y b ) en una función T a -> T b . Esta función de map también debe obedecer las leyes de identidad y composición de tal manera que las siguientes expresiones devuelvan verdaderas para todos p y q (notación Haskell):

map id = id map (p . q) = map p . map q

Por ejemplo, un constructor de tipo llamado List es un functor si viene equipado con una función de tipo (a -> b) -> List a -> List b que obedece las leyes anteriores. La única implementación práctica es obvia. La función resultante List a -> List b itera sobre la lista dada, llamando a la función (a -> b) para cada elemento, y devuelve la lista de resultados.

Una mónada es esencialmente un functor T con dos métodos adicionales, join , de tipo T (T a) -> T a , y unit (a veces llamado return , fork , o pure ) de tipo a -> T a . Para listas en Haskell:

join :: [[a]] -> [a] pure :: a -> [a]

¿Por qué es útil? Porque podría, por ejemplo, map una lista con una función que devuelve una lista. Join toma la lista resultante de listas y las concatena. List es una mónada porque esto es posible.

Puede escribir una función que haga un map y luego join . Esta función se llama bind , o flatMap , o (>>=) , o (=<<) . Así es normalmente como se da una instancia de mónada en Haskell.

Una mónada tiene que cumplir ciertas leyes, a saber, que la join debe ser asociativa. Esto significa que si tiene un valor x de tipo [[[a]]] entonces join (join x) debería ser igual a join (map join x) . Y pure debe ser una identidad para join modo que join (pure x) == x .


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.


[Descargo de responsabilidad: todavía estoy tratando de asimilar por completo a las mónadas. Lo siguiente es justo lo que he entendido hasta ahora. Si está mal, espero que alguien con conocimiento me llame a la alfombra.]

Arnar escribió:

Las mónadas son simplemente una forma de envolver cosas y proporcionar métodos para realizar operaciones en las cosas envueltas sin desenvolverlas.

Eso es precisamente eso. La idea es así:

  1. Tomas algún tipo de valor y lo envuelves con información adicional. Al igual que el valor es de cierto tipo (por ejemplo, un entero o una cadena), la información adicional es de cierto tipo.

    Por ejemplo, esa información adicional podría ser un Maybe o un IO .

  2. Luego tiene algunos operadores que le permiten operar con los datos empaquetados mientras transporta esa información adicional. Estos operadores usan la información adicional para decidir cómo cambiar el comportamiento de la operación en el valor envuelto.

    Por ejemplo, un Maybe Int puede ser un Just Int o Nothing . Ahora, si agrega un Maybe Int a un Maybe Int , el operador verificará si ambos están dentro de Just Int y, de ser así, desenvolverá los Int s, pasará el operador de suma y volverá a envolver el Int resultante en un nuevo Just Int (que es un Maybe Int válido) y, por lo tanto, devuelve un Maybe Int . Pero si uno de ellos era un Nothing dentro, este operador devolverá inmediatamente Nothing , que nuevamente es un Maybe Int . De esa manera, puede fingir que sus Maybe Int son solo números normales y realizar cálculos matemáticos regulares sobre ellos. Si obtuvieras un Nothing , tus ecuaciones seguirán produciendo el resultado correcto, sin que tengas que tirar cheques por Nothing todas partes .

Pero el ejemplo es justo lo que sucede para Maybe . Si la información adicional fuera un IO , entonces se llamaría a ese operador especial definido para IO s, y podría hacer algo totalmente diferente antes de realizar la adición. (OK, agregar dos IO Int s juntos probablemente no tenga sentido, aún no estoy seguro). (Además, si prestó atención al ejemplo de Maybe , habrá notado que "ajustar un valor con cosas adicionales" no siempre es correcto. Pero es difícil ser exacto, correcto y preciso sin ser inescrutable).

Básicamente, "mónada" significa más o menos "patrón" . Pero en lugar de un libro lleno de Patrones explicados informalmente y específicamente nombrados, ahora tiene una construcción de lenguaje , sintaxis y todo, que le permite declarar nuevos patrones como elementos en su programa . (La imprecisión aquí es que todos los patrones tienen que seguir una forma particular, por lo que una mónada no es tan genérica como un patrón. Pero creo que ese es el término más cercano que la mayoría de la gente conoce y entiende).

Y es por eso que la gente encuentra a las mónadas tan confusas: porque son un concepto tan genérico. Preguntar qué hace que algo sea una mónada es igualmente vago como preguntar qué hace que algo sea un patrón.

Pero piense en las implicaciones de tener un soporte sintáctico en el lenguaje para la idea de un patrón: en lugar de tener que leer el libro Gang of Four y memorizar la construcción de un patrón en particular, simplemente escriba código que implemente este patrón de manera agnóstica, forma genérica una vez y ya está! Luego puede reutilizar este patrón, como Visitor o Strategy o Façade o lo que sea, simplemente decorando las operaciones en su código con él, ¡sin tener que volver a implementarlo una y otra vez!

Es por eso que las personas que entienden las mónadas las encuentran tan útiles : no es un concepto de torre de marfil del que los snobs intelectuales se enorgullecen de comprender (OK, eso también, por supuesto, teehee), sino que en realidad simplifica el código.




(Ver también las respuestas en ¿Qué es una mónada? )

¡Una buena motivación para las mónadas es sigfpe (Dan Piponi), ¡ Podrías haber inventado mónadas! (Y tal vez ya lo tengas) . Hay MUCHOS otros tutoriales de mónadas , muchos de los cuales tratan erróneamente de explicar las mónadas en "términos simples" usando varias analogías: esta es la falacia del tutorial de mónada ; Evítales.

Como dice DR MacIver en Cuéntanos por qué apesta tu idioma :

Entonces, cosas que odio de Haskell:

Comencemos con lo obvio. Mónada tutoriales. No, no mónadas. Específicamente los tutoriales. Son interminables, exagerados y querido Dios, son tediosos. Además, nunca he visto ninguna evidencia convincente de que realmente ayuden. Lea la definición de clase, escriba un código, supere el nombre aterrador.

¿Dices que entiendes a la Mónada Quizás? Bien, ya estás en camino. Simplemente comience a usar otras mónadas y, tarde o temprano, comprenderá qué son las mónadas en general.

[Si está orientado matemáticamente, es posible que desee ignorar las docenas de tutoriales y aprender la definición, o seguir conferencias en teoría de categorías :) La parte principal de la definición es que una Mónada M involucra un "constructor de tipos" que define para cada tipo existente "T", un nuevo tipo "MT", y algunas formas de ir y venir entre los tipos "normales" y los tipos "M".]

Además, sorprendentemente, una de las mejores introducciones a las mónadas es en realidad uno de los primeros trabajos académicos que presenta a las mónadas, las mónadas de Philip Wadler para la programación funcional . En realidad, tiene ejemplos prácticos y no triviales de motivación, a diferencia de muchos de los tutoriales artificiales que existen.


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).


Esta respuesta comienza con un ejemplo motivador, funciona a través del ejemplo, deriva un ejemplo de una mónada y define formalmente "mónada".

Considere estas tres funciones en pseudocódigo:

f(<x, messages>) := <x, messages "called f. "> g(<x, messages>) := <x, messages "called g. "> wrap(x) := <x, "">

ftoma un par ordenado del formulario <x, messages>y devuelve un par ordenado. Deja el primer elemento intacto y se agrega "called f. "al segundo elemento. Lo mismo con g.

Puede componer estas funciones y obtener su valor original, junto con una cadena que muestra en qué orden se llamaron las funciones:

f(g(wrap(x))) = f(g(<x, "">)) = f(<x, "called g. ">) = <x, "called g. called f. ">

Que no le gusta el hecho de que fy ges responsable de sus propios añadiendo mensajes de registro de la información de registro anterior. (Solo imagínelo por el argumento de que, en lugar de agregar cadenas, fy gdebe realizar una lógica complicada en el segundo elemento del par. Sería un dolor repetir esa lógica complicada en dos, o más, funciones diferentes).

Prefieres escribir funciones más simples:

f(x) := <x, "called f. "> g(x) := <x, "called g. "> wrap(x) := <x, "">

Pero mira lo que sucede cuando los compones:

f(g(wrap(x))) = f(g(<x, "">)) = f(<<x, "">, "called g. ">) = <<<x, "">, "called g. ">, "called f. ">

El problema es que pasar un par a una función no te da lo que quieres. Pero, ¿y si pudieras alimentar un par en una función?

feed(f, feed(g, wrap(x))) = feed(f, feed(g, <x, "">)) = feed(f, <x, "called g. ">) = <x, "called g. called f. ">

Leer feed(f, m)como "alimentar men f". Para alimentar a una pareja <x, messages>en una función fes a pasar x en f, obtener <y, message>fuera de f, y volver <y, messages message>.

feed(f, <x, messages>) := let <y, message> = f(x) in <y, messages message>

Observe lo que sucede cuando hace tres cosas con sus funciones:

Primero: si ajusta un valor y luego alimenta el par resultante en una función:

feed(f, wrap(x)) = feed(f, <x, "">) = let <y, message> = f(x) in <y, "" message> = let <y, message> = <x, "called f. "> in <y, "" message> = <x, "" "called f. "> = <x, "called f. "> = f(x)

Eso es lo mismo que pasar el valor a la función.

Segundo: si alimentas un par en wrap:

feed(wrap, <x, messages>) = let <y, message> = wrap(x) in <y, messages message> = let <y, message> = <x, ""> in <y, messages message> = <x, messages ""> = <x, messages>

Eso no cambia la pareja.

Tercero: si se define una función que toma xy se alimenta g(x)en f:

h(x) := feed(f, g(x))

y alimentar un par en él:

feed(h, <x, messages>) = let <y, message> = h(x) in <y, messages message> = let <y, message> = feed(f, g(x)) in <y, messages message> = let <y, message> = feed(f, <x, "called g. ">) in <y, messages message> = let <y, message> = let <z, msg> = f(x) in <z, "called g. " msg> in <y, messages message> = let <y, message> = let <z, msg> = <x, "called f. "> in <z, "called g. " msg> in <y, messages message> = let <y, message> = <x, "called g. " "called f. "> in <y, messages message> = <x, messages "called g. " "called f. "> = feed(f, <x, messages "called g. ">) = feed(f, feed(g, <x, messages>))

Es lo mismo que alimentar al par gy alimentar al par resultante f.

Tienes la mayor parte de una mónada. Ahora solo necesita saber acerca de los tipos de datos en su programa.

¿Qué tipo de valor es <x, "called f. ">? Bueno, eso depende de qué tipo de valor xsea. Si xes de tipo t, entonces su par es un valor de tipo "par de ty cadena". Llama a ese tipo M t.

Mes un constructor de tipos: Msolo no se refiere a un tipo, sino que se M _refiere a un tipo una vez que completa el espacio en blanco con un tipo. Un M intes un par de int y una cadena. Un M stringes un par de una cadena y una cadena. Etc.

¡Felicitaciones, has creado una mónada!

Formalmente, tu mónada es la tupla <M, feed, wrap>.

Una mónada es una tupla <M, feed, wrap>donde:

  • M es un constructor de tipos.
  • feedtoma a (función que toma a ty devuelve an M u) y an M ty devuelve an M u.
  • wraptoma un vy devuelve un M v.

t, uy vson tres tipos que pueden ser o no iguales. Una mónada satisface las tres propiedades que probó para su mónada específica:

  • Alimentar un envoltorio ten una función es lo mismo que pasar el envoltorio ta la función.

    Formalmente: feed(f, wrap(x)) = f(x)

  • La alimentación de un M ta wrapno hace nada para el M t.

    Formalmente: feed(wrap, m) = m

  • Alimentando un M t(llámelo m) en una función que

    • pasa talg
    • recibe un M u(llámalo n) deg
    • alimenta nenf

    es lo mismo que

    • alimentando mag
    • obteniendo ndeg
    • alimentando naf

    Formalmente: feed(h, m) = feed(f, feed(g, m))dondeh(x) := feed(f, g(x))

Por lo general, feedse llama bind(AKA >>=en Haskell) y wrapse llama return.


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.


Lo que el mundo necesita es otra publicación de blog de mónada, pero creo que esto es útil para identificar mónadas existentes en la naturaleza.

Lo anterior es un fractal llamado triángulo de Sierpinski, el único fractal que puedo recordar dibujar. Los fractales son estructuras auto-similares como el triángulo anterior, en el cual las partes son similares al todo (en este caso, exactamente la mitad de la escala como triángulo padre).

Las mónadas son fractales. Dada una estructura de datos monádica, sus valores se pueden componer para formar otro valor de la estructura de datos. Es por eso que es útil para la programación, y es por eso que ocurre en muchas situaciones.


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.


Deje que el " {| a |m}" siguiente represente algún dato monádico. Un tipo de datos que anuncia un a:

(I got an a!) / {| a |m}

La función, fsabe cómo crear una mónada, si solo tuviera un a:

(Hi f! What should I be?) / (You?. Oh, you''ll be / that data there.) / / / (I got a b.) | -------------- | | / | f a | |--later-> {| b |m}

Aquí vemos la función, fintenta evaluar una mónada pero es reprendido.

(Hmm, how do I get that a?) o (Get lost buddy. o Wrong type.) o / f {| a |m}

Funtion, fencuentra una forma de extraer el amediante >>=.

(Muaahaha. How you like me now!?) (Better.) / | (Give me that a.) (Fine, well ok.) | / | {| a |m} >>= f

Poco fsabe, la mónada y >>=están en colusión.

(Yah got an a for me?) (Yeah, but hey | listen. I got | something to | tell you first | ...) / / | / {| a |m} >>= f

¿Pero de qué hablan realmente? Bueno, eso depende de la mónada. Hablar únicamente en abstracto tiene un uso limitado; tienes que tener alguna experiencia con mónadas particulares para desarrollar el entendimiento.

Por ejemplo, el tipo de datos Quizás

data Maybe a = Nothing | Just a

tiene una instancia de mónada que actuará de la siguiente manera ...

En donde, si el caso es Just a

(Yah what is it?) (... hm? Oh, | forget about it. | Hey a, yr up.) | / | (Evaluation / | time already? / | Hows my hair?) | | | / | | (It''s | | fine.) / | / / {| a |m} >>= f

Pero para el caso de Nothing

(Yah what is it?) (... There | is no a. ) | | (No a?) (No a.) | | (Ok, I''ll deal | with this.) / | / (Hey f, get lost.) / | ( Where''s my a? / | I evaluate a) / (Not any more | / you don''t. | | We''re returning | Nothing.) / | | / | | / | | / {| a |m} >>= f (I got a b.) | (This is / | such a / | sham.) o o / | o| |--later-> {| b |m}

Entonces, la mónada tal vez permite que un cálculo continúe si realmente contiene lo aque anuncia, pero aborta el cálculo si no lo hace. El resultado, sin embargo, sigue siendo un dato monádico, aunque no el resultado de f. Por esta razón, la mónada Quizás se usa para representar el contexto del fracaso.

Las diferentes mónadas se comportan de manera diferente. Las listas son otros tipos de datos con instancias monádicas. Se comportan de la siguiente manera:

(Ok, here''s your a. Well, its a bunch of them, actually.) | | (Thanks, no problem. Ok | f, here you go, an a.) | | | | (Thank''s. See | | you later.) | (Whoa. Hold up f, | | I got another | | a for you.) | | | (What? No, sorry. | | Can''t do it. I | | have my hands full | | with all these "b" | | I just made.) | (I''ll hold those, | | you take this, and / | come back for more / | when you''re done / | and we''ll do it / | again.) / / | ( Uhhh. All right.) / | / / / / {| a |m} >>= f

En este caso, la función sabía cómo hacer una lista a partir de su entrada, pero no sabía qué hacer con entradas adicionales y listas adicionales. El enlace >>=, ayudado fal combinar las múltiples salidas. Incluyo este ejemplo para mostrar que, si bien >>=es responsable de la extracción a, también tiene acceso a la salida eventual de f. De hecho, nunca extraerá ninguno a amenos que sepa que el resultado final tiene el mismo tipo de contexto.

Hay otras mónadas que se utilizan para representar diferentes contextos. Aquí hay algunas caracterizaciones de algunas más. La IOmónada en realidad no tiene un a, pero conoce a un chico y lo conseguirá apor ti. La State stmónada tiene un alijo secreto de stque pasará fdebajo de la mesa, a pesar de que facaba de llegar pidiendo un a. La Reader rmónada es similar a State st, a pesar de que sólo deja fver r.

El punto en todo esto es que cualquier tipo de datos que se declare a sí mismo como una mónada está declarando algún tipo de contexto en torno a la extracción de un valor de la mónada. ¿La gran ganancia de todo esto? Bueno, es bastante fácil armar un cálculo con algún tipo de contexto. Sin embargo, puede volverse desordenado al unir varios cálculos cargados de contexto. Las operaciones de mónada se encargan de resolver las interacciones de contexto para que el programador no tenga que hacerlo.

Tenga en cuenta que el uso de >>=alivia un desastre al quitarle algo de autonomía f. Es decir, en el caso anterior, Nothingpor ejemplo, fya no se puede decidir qué hacer en el caso de Nothing; se codifica en >>=. Esta es la compensación. Si fue necesario fdecidir qué hacer en el caso de Nothing, entonces fdebería haber sido una función desde Maybe ahasta Maybe b. En este caso, Maybeser una mónada es irrelevante.

Sin embargo, tenga en cuenta que a veces un tipo de datos no exporta sus constructores (mirándolo IO), y si queremos trabajar con el valor anunciado, tenemos pocas opciones, pero trabajar con su interfaz monádica.


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.


Monoid parece ser algo que garantiza que todas las operaciones definidas en un Monoid y un tipo compatible siempre devolverán un tipo compatible dentro del Monoid. Por ejemplo, Cualquier número + Cualquier número = Un número, sin errores.

Mientras que la división acepta dos fraccionarios, y devuelve un fraccionario, que definió la división por cero como Infinito en haskell somewhy (que resulta ser un fraccional somewhy) ...

En cualquier caso, parece que las mónadas son solo una forma de garantizar que su cadena de operaciones se comporte de una manera predecible, y una función que dice ser Num -> Num, compuesta con otra función de Num-> Num llamada con x no digamos, dispara los misiles.

Por otro lado, si tenemos una función que dispara los misiles, podemos componerla con otras funciones que también disparan los misiles, porque nuestra intención es clara: queremos disparar los misiles, pero no lo intentaremos imprimiendo "Hello World" por alguna extraña razón.

En Haskell, main es de tipo IO (), o IO [()], la discreción es extraña y no lo discutiré, pero esto es lo que creo que sucede:

Si tengo main, quiero que haga una cadena de acciones, la razón por la que ejecuto el programa es para producir un efecto, generalmente a través de IO. Por lo tanto, puedo encadenar las operaciones de IO en main para - hacer IO, nada más.

Si trato de hacer algo que no "devuelve IO", el programa se quejará de que la cadena no fluye, o básicamente "¿Cómo se relaciona esto con lo que estamos tratando de hacer? Una acción de IO", parece forzar el programador debe mantener su línea de pensamiento, sin desviarse y sin pensar en disparar los misiles, mientras crea algoritmos para la clasificación, que no fluye.

Básicamente, las mónadas parecen ser una sugerencia para el compilador que "oye, conoces esta función que devuelve un número aquí, en realidad no siempre funciona, a veces puede producir un número, y a veces nada en absoluto, solo mantén esto en mente". Sabiendo esto, si intentas afirmar una acción monádica, la acción monádica puede actuar como una excepción de tiempo de compilación que dice "oye, esto no es realmente un número, PUEDE ser un número, pero no puedes asumir esto, haz algo para garantizar que el flujo sea aceptable ". lo que evita un comportamiento impredecible del programa, en buena medida.

Parece que las mónadas no tienen que ver con la pureza, ni con el control, sino con el mantenimiento de una identidad de una categoría en la que todo comportamiento es predecible y definido, o no se compila. No puede hacer nada cuando se espera que haga algo, y no puede hacer algo si se espera que no haga nada (visible).

La razón más importante por la que puedo pensar para Monads es: ve a ver el código de procedimiento / OOP, y notarás que no sabes dónde comienza o termina el programa, todo lo que ves es un montón de saltos y muchas matemáticas , magia y misiles. No podrá mantenerlo, y si puede, pasará bastante tiempo concentrando su mente en todo el programa antes de que pueda comprender cualquier parte del mismo, porque la modularidad en este contexto se basa en "secciones" interdependientes de código, donde el código está optimizado para estar lo más relacionado posible para la promesa de eficiencia / interrelación. Las mónadas son muy concretas y están bien definidas por definición, y aseguran que el flujo del programa sea posible de analizar y aislar partes que son difíciles de analizar, ya que ellas mismas son mónadas. Una mónada parece ser un "unidad comprensible que es predecible a partir de su plena comprensión "- Si comprende la mónada" Quizás ", no hay forma posible de que haga nada excepto ser" Quizás ", que parece trivial, pero en la mayoría de los códigos no monádicos, una función simple" helloworld "puede disparar los misiles, no hacer nada, o destruir el universo o incluso distorsionar el tiempo. No tenemos ni idea ni garantías de que ES LO QUE ES. Una mónada GARANTIZA que ES LO QUE ES. lo cual es muy poderoso".o destruir el universo o incluso distorsionar el tiempo; no tenemos ni idea ni garantías de que ES LO QUE ES. Una mónada GARANTIZA QUE ES LO QUE ES. que es muy poderosoo destruir el universo o incluso distorsionar el tiempo; no tenemos ni idea ni garantías de que ES LO QUE ES. Una mónada GARANTIZA QUE ES LO QUE ES. que es muy poderoso

Todas las cosas en el "mundo real" parecen ser mónadas, en el sentido de que está sujeto a leyes observables definidas que evitan la confusión. Esto no significa que tengamos que imitar todas las operaciones de este objeto para crear clases, sino que simplemente podemos decir "un cuadrado es un cuadrado", nada más que un cuadrado, ni siquiera un rectángulo ni un círculo, y "un cuadrado tiene área de la longitud de una de sus dimensiones existentes multiplicada por sí misma. No importa qué cuadrado tenga, si es un cuadrado en el espacio 2D, su área no puede ser más que su longitud al cuadrado, es casi trivial probarlo. Esto es muy poderoso porque no necesitamos hacer afirmaciones para asegurarnos de que nuestro mundo sea como es, solo usamos las implicaciones de la realidad para evitar que nuestros programas se salgan del camino.

Estoy bastante seguro de estar equivocado, pero creo que esto podría ayudar a alguien, así que espero que ayude a alguien.



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.


Trataré de explicar Monaden el contexto de Haskell.

En la programación funcional, la composición de funciones es importante. Permite que nuestro programa consista en funciones pequeñas y fáciles de leer.

Digamos que tenemos dos funciones: g :: Int -> Stringy f :: String -> Bool.

Podemos hacer (f . g) x, que es lo mismo que f (gx), donde xhay un Intvalor.

Al hacer la composición / aplicar el resultado de una función a otra, es importante que los tipos coincidan. En el caso anterior, el tipo del resultado devuelto por gdebe ser el mismo que el tipo aceptado por f.

Pero a veces los valores están en contextos, y esto hace que sea un poco menos fácil alinear tipos. (Tener valores en contextos es muy útil. Por ejemplo, el Maybe Inttipo representa un Intvalor que puede no estar allí, el IO Stringtipo representa un Stringvalor que está allí como resultado de realizar algunos efectos secundarios).

Digamos que ahora tenemos g1 :: Int -> Maybe Stringy f1 :: String -> Maybe Bool. g1y f1son muy similares a gy frespectivamente.

No podemos hacer (f1 . g1) xo f1 (g1 x), donde xhay un Intvalor. El tipo de resultado devuelto por g1no es el que se f1espera.

Podríamos componer fy gcon el .operador, pero ahora no podemos componer f1y g1con. . El problema es que no podemos pasar directamente un valor en un contexto a una función que espera un valor que no está en un contexto.

¿No sería bueno si presentamos un operador para componer g1y f1, de modo que podamos escribir (f1 OPERATOR g1) x? g1devuelve un valor en un contexto. El valor se sacará de contexto y se aplicará a f1. Y sí, tenemos un operador así. Es <=<.

También tenemos el >>=operador que hace por nosotros exactamente lo mismo, aunque en una sintaxis ligeramente diferente.

Escribimos: g1 x >>= f1. g1 xes un Maybe Intvalor El >>=operador ayuda a sacar ese Intvalor del contexto "quizás no esté" y aplicarlo f1. El resultado de f1, que es a Maybe Bool, será el resultado de toda la >>=operación.

Y finalmente, ¿por qué es Monadútil? Porque Monades la clase de tipo que define el >>=operador, muy similar a la Eqclase de tipo que define los operadores ==y /=.

Para concluir, la Monadclase de tipo define el >>=operador que nos permite pasar valores en un contexto (los denominamos valores monádicos) a funciones que no esperan valores en un contexto. El contexto será atendido.

Si hay algo que recordar aquí, es que Monadpermite la composición de funciones que involucra valores en contextos .


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.


Una mónada es, efectivamente, una forma de "operador de tipo". Hará tres cosas. Primero "envolverá" (o convertirá) un valor de un tipo en otro tipo (típicamente llamado "tipo monádico"). En segundo lugar, hará que todas las operaciones (o funciones) estén disponibles en el tipo subyacente disponible en el tipo monádico. Finalmente proporcionará soporte para combinarse con otra mónada para producir una mónada compuesta.

La "quizás mónada" es esencialmente el equivalente de "tipos anulables" en Visual Basic / C #. Toma un tipo "T" no anulable y lo convierte en un "Nullable <T>", y luego define lo que significan todos los operadores binarios en un Nullable <T>.

Los efectos secundarios se representan de manera similar. Se crea una estructura que contiene descripciones de los efectos secundarios junto con el valor de retorno de una función. Las operaciones "levantadas" luego copian los efectos secundarios a medida que se pasan valores entre funciones.

Se llaman "mónadas" en lugar del nombre más fácil de entender de "operadores de tipo" por varias razones:

  1. Las mónadas tienen restricciones sobre lo que pueden hacer (ver la definición para más detalles).
  2. Esas restricciones, junto con el hecho de que hay tres operaciones involucradas, se ajustan a la estructura de algo llamado mónada en la teoría de la categoría, que es una rama oscura de las matemáticas.
  3. Fueron diseñados por defensores de lenguajes funcionales "puros"
  4. Los defensores de lenguajes funcionales puros como ramas oscuras de las matemáticas.
  5. Debido a que las matemáticas son oscuras y las mónadas están asociadas con estilos particulares de programación, las personas tienden a usar la palabra mónada como una especie de apretón de manos secreto. Debido a esto, nadie se ha molestado en invertir en un mejor nombre.