¿Qué tiene de especial la Mónada?


Una mónada es una estructura matemática que es muy utilizada en la programación funcional (pura), básicamente Haskell. Sin embargo, hay muchas otras estructuras matemáticas disponibles, como por ejemplo funtores aplicativos, mónadas fuertes o monoides. Algunos tienen más específicos, otros son más genéricos. Sin embargo, las mónadas son mucho más populares. ¿Por qué es eso?

Una explicación que se me ocurrió es que son un punto dulce entre la genericidad y la especificidad. Esto significa que las mónadas capturan suficiente las suposiciones sobre los datos para aplicar los algoritmos que normalmente usamos y los datos que generalmente tenemos cumplen con las leyes monádicas.

Otra explicación podría ser que Haskell proporciona sintaxis para mónadas (do-notation), pero no para otras estructuras, lo que significa que los programadores de Haskell (y por lo tanto los investigadores de programación funcional) son intuitivamente atraídos hacia las mónadas, donde una función más genérica o específica (eficiente) también funcionaría.

Author: Community, 2013-05-08

5 answers

Sospecho que la atención desproporcionadamente grande dada a esta clase de tipo particular (Monad) sobre las muchas otras es principalmente una casualidad histórica. La gente a menudo asocia IO con Monad, aunque las dos son ideas útiles independientemente ( al igual que la reversión de listas y los plátanos). Debido a que IO es mágico (tiene una implementación pero no una denotación) y Monad a menudo se asocia con IO, es fácil caer en el pensamiento mágico sobre Monad.

(Aparte: es cuestionable si IO incluso es una mónada. ¿Se cumplen las leyes de la mónada? ¿Qué significan las leyes aún para IO, es decir, qué significa la igualdad? Note la asociación problemática con la mónada de estado.)

 28
Author: Conal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-12 07:31:17

Si un tipo m :: * -> * tiene una instancia Monad, se obtiene Turing-composición completa de funciones con el tipo a -> m b. Esta es una propiedad fantásticamente útil. Obtienes la capacidad de abstraer varios flujos de control completo de Turing lejos de significados específicos. Es un patrón de composición mínima que admite la abstracción de cualquier flujo de control para trabajar con tipos que lo soportan.

Compare esto con Applicative, por ejemplo. Allí, solo se obtienen patrones de composición con potencia computacional equivalente a un autómata. Por supuesto, es cierto que más tipos soportan la composición con una potencia más limitada. Y es cierto que cuando limita la potencia disponible, puede hacer optimizaciones adicionales. Estas dos razones son por qué la clase Applicative existe y es útil. Pero las cosas que pueden ser instancias de Monad generalmente lo son, para que los usuarios del tipo puedan realizar las operaciones más generales posibles con el tipo.

Editar: Por demanda popular, aquí hay algunas funciones que utilizan el Monad clase:

ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM c x y = c >>= \z -> if z then x else y

whileM :: Monad m => (a -> m Bool) -> (a -> m a) -> a -> m a
whileM p step x = ifM (p x) (step x >>= whileM p step) (return x)

(*&&) :: Monad m => m Bool -> m Bool -> m Bool
x *&& y = ifM x y (return False)

(*||) :: Monad m => m Bool -> m Bool -> m Bool
x *|| y = ifM x (return True) y

notM :: Monad m => m Bool -> m Bool
notM x = x >>= return . not

La combinación de estos con la sintaxis do (o el operador raw >>=) le da un enlace de nombres, un bucle indefinido y una lógica booleana completa. Ese es un conjunto bien conocido de primitivas suficientes para dar Turing integridad. Observe cómo todas las funciones han sido levantadas para trabajar en valores monádicos, en lugar de valores simples. Todos los efectos monádicos se vinculan solo cuando es necesario-solo los efectos de la rama elegida de ifM se vinculan a su valor final. Tanto *&& como *|| ignorar su segundo argumento cuando sea posible. Y así sucesivamente..

Ahora, esas firmas de tipo pueden no involucrar funciones para cada operando monádico, pero eso es solo una simplificación cognitiva. No habría diferencia semántica, ignorando los fondos, si todos los argumentos y resultados no funcionales se cambiaran a () -> m a. Es más amigable para los usuarios optimizar esa sobrecarga cognitiva.

Ahora, veamos lo que sucede con esas funciones con el Applicative interfaz.

ifA :: Applicative f => f Bool -> f a -> f a -> f a
ifA c x y = (\c' x' y' -> if c' then x' else y') <$> c <*> x <*> y

Bueno, uh. Tiene la misma firma de tipo. Pero ya hay un gran problema aquí. Los efectos de x e y están vinculados a la estructura compuesta, independientemente de cuál sea el valor seleccionado.

whileA :: Applicative f => (a -> f Bool) -> (a -> f a) -> a -> f a
whileA p step x = ifA (p x) (whileA p step <$> step x) (pure x)

Bueno, bueno, eso parece que estaría bien, excepto por el hecho de que es un bucle infinito porque ifA siempre ejecutará ambas ramas... Excepto que ni siquiera está tan cerca. pure x tiene el tipo f a. whileA p step <$> step x tiene el tipo f (f a). Esto ni siquiera es un bucle infinito. Es un error de compilación. Intentémoslo de nuevo..

whileA :: Applicative f => (a -> f Bool) -> (a -> f a) -> a -> f a
whileA p step x = ifA (p x) (whileA p step <*> step x) (pure x)

Bien disparar. Ni siquiera llegues tan lejos. whileA p step tiene el tipo a -> f a. Si intenta usarlo como primer argumento para <*>, toma la instancia Applicative para el constructor de tipo top, que es (->), no f. Sí, esto tampoco va a funcionar.

De hecho, la única función de mis ejemplos Monad que funcionaría con la interfaz Applicative es notM. Esa función en particular funciona bien con solo un Functor interfaz, de hecho. El resto? Fracasan.

Por supuesto, es de esperar que pueda escribir código usando la interfaz Monad que no puede con la interfaz Applicative. Es estrictamente más poderoso, después de todo. Pero lo interesante es lo que pierdes. Se pierde la capacidad de componer funciones que cambian los efectos que tienen en función de su entrada. Es decir, pierde la capacidad de escribir ciertos patrones de flujo de control que componen funciones con tipos a -> f b.

Turing-la composición completa es exactamente lo que hace interesante la interfaz Monad. Si no permitiera la composición completa de Turing, sería imposible para usted, el programador, componer juntos IO acciones en cualquier flujo de control en particular que no estuviera bien preempaquetado para usted. Fue el hecho de que puede usar las primitivas Monad para expresar cualquier flujo de control lo que hizo que el tipo IO fuera una forma factible de administrar el problema de E / s en Haskell.

Muchos más tipos que solo IO tienen interfaces Monad semánticamente válidas. Y sucede que Haskell tiene las facilidades de lenguaje para abstraer sobre toda la interfaz. Debido a estos factores, Monad es una clase valiosa para proporcionar instancias, cuando sea posible. Al hacerlo, obtiene acceso a toda la funcionalidad abstracta existente proporcionada para trabajar con tipos monádicos, independientemente de cuál sea el tipo concreto.

Así que si los programadores de Haskell parecen siempre preocuparse Monad instancias para un tipo, es porque es la instancia más genéricamente útil que se puede proporcionar.

 15
Author: Carl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-17 20:48:28

Primero, creo que no es del todo cierto que las mónadas sean mucho más populares que cualquier otra cosa; tanto Funtor como Monoid tienen muchas instancias que no son mónadas. Pero ambos son muy específicos; Functor proporciona mapeo, concatenación monoide. Aplicativo es la única clase que puedo pensar que es probablemente infrautilizado dado su considerable poder, debido en gran parte a su ser una adición relativamente reciente a la lengua.

Pero sí, las mónadas son extremadamente populares. Parte de eso es el do notación; muchos Monoides proporcionan instancias de mónadas que simplemente anexan valores a un acumulador en ejecución (esencialmente un escritor implícito). La biblioteca blaze-html es un buen ejemplo. La razón, creo, es el poder de la firma de tipo (>>=) :: Monad m => m a -> (a -> m b) -> m b. Si bien fmap y mappend son útiles, lo que pueden hacer es bastante limitado. bind, sin embargo, puede expresar una amplia variedad de cosas. Es, por supuesto, canonizado en la mónada IO, tal vez el mejor enfoque funcional puro para IO antes de corrientes y FRP (y sigue siendo útil junto a ellos para tareas simples y definir componentes). Pero también proporciona un estado implícito (Reader / Writer / ST), que puede evitar el paso de variables muy tedioso. Las diversas mónadas de estado, especialmente, son importantes porque proporcionan una garantía de que el estado es un subproceso simple, permitiendo estructuras mutables en código puro (no-IO) antes de la fusión. Pero bind tiene algunos usos más exóticos, como aplanar estructuras de datos anidadas (las mónadas List y Set), las cuales son bastante útiles en su lugar (y normalmente los veo usar desugared, llamando a liftM o ( > > = ) explícitamente, por lo que no es una cuestión de notación do). Así que mientras Funtor y Monoid (y el algo más raro Plegable, Alternativa, Transitable, y otros) proporcionan una interfaz estandarizada para una función bastante sencilla, enlace de Monad es considerablemente más flexibilidad.

En resumen, creo que todas sus razones tienen algún papel; la popularidad de las mónadas se debe a una combinación de accidente histórico (do notation y la definición tardía de Aplicativo) y su combinación de poder y generalidad (en relación con funtores, monoides y similares) y comprensibilidad (en relación con flechas).

 12
Author: isturdy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-08 15:26:57

Bueno, primero permítanme explicar cuál es el papel de las mónadas: Las mónadas son muy poderosas, pero en cierto sentido: Puedes expresar cualquier cosa usando una mónada. Haskell como lenguaje no tiene cosas como bucles de acción, excepciones, mutación, goto, etc. Las mónadas se pueden expresar dentro del lenguaje (por lo que no son especiales) y hacer que todas estas sean accesibles.

Hay un lado positivo y un lado negativo de esto: Es positivo que puedas expresar todas esas estructuras de control que conoces recientemente he desarrollado una mónada que te permite volver a ingresar un cálculo en algún lugar en el medio con un contexto ligeramente cambiado. De esa manera se puede ejecutar un cálculo, y si falla, solo inténtelo de nuevo con valores ligeramente ajustados. Además, las acciones monádicas son de primera clase, y así es como construyes cosas como bucles o manejo de excepciones. Mientras que while es primitivo en C en Haskell, en realidad es solo un función .

El lado negativo es que las mónadas no te dan prácticamente ninguna garantía. Son tan poderosos que se les permite hacer lo que quieran, en pocas palabras. En otras palabras, al igual que usted sabe de los lenguajes imperativos, puede ser difícil razonar sobre el código con solo mirarlo.

Las abstracciones más generales son más generales en el sentido de que permiten que se expresen algunos conceptos que no se pueden expresar como mónadas. Pero eso es solo una parte de la historia. Incluso para mónadas puede usar un estilo conocido como estilo aplicativo, en el que usa la interfaz aplicativa para componer su programa a partir de pequeñas partes aisladas. El beneficio de esto es que puede razonar sobre el código con solo mirarlo y puede desarrollar componentes sin tener que prestar atención al resto de su sistema.

 6
Author: ertes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-08 11:35:16

Las mónadas son especiales debido a la notación do, que le permite escribir programas imperativos en un lenguaje funcional. Monad es la abstracción que le permite unir programas imperativos de componentes más pequeños y reutilizables (que a su vez son programas imperativos). Monad transformers son especiales porque representan la mejora de un lenguaje imperativo con nuevas características.

 0
Author: Dan Burton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-17 04:47:32