En programación funcional , una mónada es una abstracción que permite estructurar programas de forma genérica . Los lenguajes de soporte pueden usar mónadas para abstraer el código estándar que necesita la lógica del programa. Las mónadas logran esto proporcionando su propio tipo de datos (un tipo particular para cada tipo de mónada), que representa una forma específica de cálculo , junto con dos procedimientos :
- Uno para envolver valores de cualquier tipo básico dentro de la mónada (produciendo un valor monádico );
- Otro para componer funciones que generan valores monádicos (llamadas funciones monádicas ). [1]
Esto permite mónadas para simplificar una amplia gama de problemas, como el manejo de posibles valores no definidos (con la Maybe
mónada), o mantener los valores dentro de una solución flexible, bien formada lista (utilizando la List
mónada). Con una mónada, un programador puede convertir una secuencia complicada de funciones en una tubería sucinta que abstrae la administración de datos auxiliares, el flujo de control o los efectos secundarios . [1] [2]
Tanto el concepto de mónada como el término provienen originalmente de la teoría de categorías , donde una mónada se define como un funtor con estructura adicional. [a] Las investigaciones que comenzaron a fines de la década de 1980 y principios de la de 1990 establecieron que las mónadas podían generar problemas informáticos aparentemente dispares bajo un modelo funcional unificado. La teoría de categorías también proporciona algunos requisitos formales, conocidos como leyes de las mónadas , que cualquier mónada debería satisfacer y que pueden utilizarse para verificar el código monádico. [3] [4]
Dado que las mónadas hacen que la semántica sea explícita para un tipo de cálculo, también se pueden usar para implementar características de lenguaje convenientes. Algunos lenguajes, como Haskell , incluso ofrecen definiciones predefinidas en sus bibliotecas centrales para la estructura general de mónadas y las instancias comunes. [1] [5]
Descripción general
"Para una mónada m
, un valor de tipo m a
representa tener acceso a un valor de tipo a
dentro del contexto de la mónada". —CA McCann [6]
Se puede crear una mónada definiendo un constructor de tipo M y dos operaciones: return
(a menudo también llamado unidad ), que recibe un valor de tipo a
y lo envuelve en un valor monádico de tipo m a
, usando el constructor de tipo; y bind
(típicamente representado como >>=
), que recibe una función f
sobre el tipo a
y puede transformar valores monádicos que se m a
aplican f
al valor desenvuelto a
. (En la sección posterior § Derivación de functores se puede encontrar una construcción alternativa pero equivalente que usa la join
función en lugar del bind
operador ).
Con estos elementos, el programador compone una secuencia de llamadas a funciones (una "canalización") con varios operadores de enlace encadenados en una expresión. Cada llamada a función transforma su valor de entrada de tipo plano, y el operador de vinculación maneja el valor monádico devuelto, que se alimenta en el siguiente paso de la secuencia.
Entre cada par de llamadas de función compuestas, el operador de >>=
vinculación puede inyectar en el valor monádico m a
alguna información adicional que no es accesible dentro de la función f
y pasarla por la tubería. También puede ejercer un control más preciso del flujo de ejecución, por ejemplo, llamando a la función solo bajo algunas condiciones, o ejecutando las llamadas a la función en un orden particular.
Un ejemplo: Quizás
Para motivar la programación con mónadas, aquí hay un ejemplo rápido de pseudocódigo. Los valores u operaciones indefinidos son un problema particular para el que el software robusto debe prepararse y manejar con elegancia.
El primer paso hacia este objetivo podría ser crear un tipo de opción que marque un valor como portador de un valor de algún tipo T
( T
puede ser de cualquier tipo) o sin valor. Se llamará al nuevo tipo Maybe T
y los valores de ese tipo pueden contener un valor de tipo T
o ser el valor vacío Nothing
. Se llamará a un valor X
de tipo T
definido pero utilizado en el contexto de . Esto se hace para evitar confusiones al diferenciar entre los casos en los que una variable tiene un valor definido y aquellos en los que no.Maybe
Just X
datos Quizás T = Solo T o nada
Maybe T
puede entenderse como un tipo "envolvente", que envuelve el tipo T
en un nuevo tipo con manejo de excepciones incorporado, aunque no contiene información sobre la causa de una excepción.
En el siguiente código, las variables con el prefijo m
tienen el tipo Maybe T
de algún tipo T
. Por ejemplo, si una variable mx
contiene un valor, es Just x
donde la variable x
tiene el tipo T
. λx → ...
es una función anónima con el parámetro x
cuyo tipo se infiere , y ∘
es el operador de composición de la función .
Otra mejora sería si una función pudiera administrar excepciones comprobadas simples con un Maybe
tipo, cortocircuitando y regresando Nothing
una vez que un paso falla, pero devolviendo el valor correcto sin comentarios si un cálculo tiene éxito.
Una función de suma add
, que hace exactamente esto al agregar dos Maybe
valores, mx
y my
, se puede definir así:
agregar : ( Quizás Número , Quizás Número ) → Quizás Número agregar ( mx , mi ) = ... si mx es Nada entonces ... Nada más si mi es Nada entonces ... Nada más ... Solo ( x + y ) terminar si
Sin Maybe
embargo, escribir funciones que procesen valores caso por caso puede ser tedioso y solo lo será más a medida que se definan más funciones. Una operación para encadenar pasos juntos es una forma de aliviar esto, y con un operador infijo como x >>= y
, incluso puede representar intuitivamente alimentar el resultado (posiblemente indefinido) de cada paso al siguiente. Sin embargo, dado que cada resultado se inserta técnicamente en otra función , el operador tomará una función para un parámetro. Como add
ya se especifica el tipo de su valor de salida, no debería estar mal mantener al operador flexible y aceptar funciones que generan diferentes tipos de su entrada:
>> = : ( Quizás T , T → Quizás U ) → Quizás U ( mx >> = f ) = ... si mx es ( Solo x ) entonces ... f ( x ) - evalúa f (x) y return (posiblemente indefinido) salida else ... Nothing - pasa a través de un final de entrada indefinido si
Con >>=
disponible, add
ahora se puede redefinir como algo mucho más compacto:
agregar ( mx , mi ) = mx >> = λx -> ( mi >> = λy -> Solo ( x + y ))
Esto es más conciso, pero un pequeño análisis adicional revela algo aún más poderoso. Por un lado, el único papel que Just
juega add
es etiquetar un valor subyacente como también un Maybe
valor. Para enfatizar cómo Just
actúa sobre el valor subyacente envolviéndolo, también se puede redefinir como una función, llamado eta
por ahora:
eta : T → Quizás T eta ( x ) = Solo x
El panorama general es que estas dos funciones >>=
y eta
fueron diseñados para simplificar add
, pero es evidente que no dependen de las características específicas de add
en cualquier forma, sólo el Maybe
tipo. Estas funciones pueden, de hecho, aplicarse a cualquier valor y función del Maybe
tipo, independientemente de los tipos de valores subyacentes. Por ejemplo, aquí hay un operador NOT conciso de la lógica trinaria (de Kleene) que usa las mismas funciones para automatizar valores indefinidos también:
trinot : Quizás booleano → Quizás booleano trinot ( mp ) = mp >> = λp -> ( eta ∘ not ) p
Resulta que el Maybe
tipo, junto con >>=
y eta
, forma una mónada. Mientras que otras mónadas incorporarán diferentes procesos lógicos, y algunas pueden tener propiedades adicionales, todas tendrán tres componentes similares (directa o indirectamente) que siguen el esquema básico de este ejemplo. [1] [7]
Definición
La definición más común de una mónada en programación funcional, utilizada en el ejemplo anterior, se basa en realidad en una triple de Kleisli en lugar de la definición estándar de la teoría de categorías. Sin embargo, las dos construcciones resultan ser matemáticamente equivalentes, por lo que cualquiera de las definiciones producirá una mónada válida. Dados los tipos básicos T , U bien definidos , una mónada consta de tres partes:
- Un constructor de tipo M que crea un tipo monádico MT [b]
- Un convertidor de tipos , a menudo llamado unidad o retorno , que incrusta un objeto x en la mónada:
unit(x) : T → M T
[C] - Un combinador , típicamente llamado bind (como enlazar una variable ) y representado con un operador infijo
>>=
, que desenvuelve una variable monádica, luego la inserta en una función / expresión monádica, resultando en un nuevo valor monádico:(mx >>= f) : (M T, T → M U) → M U
[D]
Sin embargo, para calificar completamente como una mónada, estas tres partes también deben respetar algunas leyes:
- unit es una identidad izquierda para bind :
unit(a) >>= λx → f(x) ↔ f(a)
- unit también es una identidad correcta para bind :
ma >>= λx → unit(x) ↔ ma
- bind es esencialmente asociativo : [e]
ma >>= λx → (f(x) >>= λy → g(y)) ↔ (ma >>= λx → f(x)) >>= λy → g(y)
[1]
Algebraicamente, esto significa que cualquier mónada da lugar tanto a una categoría (llamada categoría de Kleisli ) como a un monoide en la categoría de functores (de valores a cálculos), con composición monádica como operador binario y unidad como identidad. [ cita requerida ]
Uso
El valor del patrón de mónadas va más allá de simplemente condensar código y proporcionar un vínculo con el razonamiento matemático. Cualquiera que sea el lenguaje o paradigma de programación predeterminado que utilice un desarrollador, seguir el patrón de mónada trae muchos de los beneficios de la programación puramente funcional . Al cosificar un tipo específico de cálculo, una mónada no solo encapsula los tediosos detalles de ese patrón computacional, sino que lo hace de forma declarativa , mejorando la claridad del código. Como los valores monádicos representan explícitamente no solo valores calculados, sino efectos calculados , una expresión monádica se puede reemplazar con su valor en posiciones referencialmente transparentes , al igual que las expresiones puras, lo que permite muchas técnicas y optimizaciones basadas en la reescritura . [4]
Por lo general, los programadores usarán bind para encadenar funciones monádicas en una secuencia, lo que ha llevado a algunos a describir las mónadas como "puntos y comas programables", una referencia a cuántos lenguajes imperativos usan punto y coma para separar declaraciones . [1] [5] Sin embargo, debe enfatizarse que las mónadas en realidad no ordenan los cálculos; incluso en lenguajes que los utilizan como características centrales, la composición de funciones más simple puede organizar los pasos dentro de un programa. La utilidad general de una mónada radica más bien en simplificar la estructura de un programa y mejorar la separación de preocupaciones a través de la abstracción. [4] [9]
La estructura de la mónada también puede verse como una variación de tiempo de compilación y matemática única en el patrón del decorador . Algunas mónadas pueden transmitir datos adicionales que son inaccesibles para las funciones, y algunas incluso ejercen un control más preciso sobre la ejecución, por ejemplo, solo llaman a una función bajo ciertas condiciones. Debido a que permiten a los programadores de aplicaciones implementar la lógica de dominio mientras descargan código repetitivo en módulos previamente desarrollados, las mónadas pueden incluso considerarse una herramienta para la programación orientada a aspectos . [10]
Otro uso digno de mención para las mónadas es aislar efectos secundarios, como entrada / salida o estado mutable , en un código puramente funcional. Incluso los lenguajes puramente funcionales aún pueden implementar estos cálculos "impuros" sin mónadas, a través de una intrincada combinación de composición de funciones y estilo de paso de continuación (CPS) en particular. [2] Sin embargo, con las mónadas, gran parte de este andamiaje se puede abstraer, esencialmente tomando cada patrón recurrente en el código CPS y empaquetándolo en una mónada distinta. [4]
Si un idioma no admite mónadas de forma predeterminada, aún es posible implementar el patrón, a menudo sin mucha dificultad. Cuando se traduce de la teoría de categorías a términos de programación, la estructura de la mónada es un concepto genérico y se puede definir directamente en cualquier lenguaje que admita una característica equivalente para el polimorfismo acotado . La capacidad de un concepto para permanecer agnóstico sobre los detalles operativos mientras se trabaja en los tipos subyacentes es poderosa, pero las características únicas y el comportamiento estricto de las mónadas los distinguen de otros conceptos. [11]
Aplicaciones
Las discusiones sobre mónadas específicas se centrarán típicamente en resolver un problema de implementación limitado, ya que una mónada determinada representa una forma computacional específica. Sin embargo, en algunas situaciones, una aplicación puede incluso alcanzar sus objetivos de alto nivel mediante el uso de mónadas apropiadas dentro de su lógica central.
Aquí hay algunas aplicaciones que tienen mónadas en el corazón de sus diseños:
- La biblioteca del analizador de Parsec utiliza mónadas para combinar reglas de análisis más simples en otras más complejas, y es particularmente útil para lenguajes específicos de dominio más pequeños . [12]
- xmonad es un administrador de ventanas en mosaico centrado en la estructura de datos de la cremallera , que a su vez puede tratarse de manera monádica como un caso específico de continuaciones delimitadas . [13]
- LINQ de Microsoft proporciona un lenguaje de consulta para .NET Framework que está fuertemente influenciado por conceptos de programación funcional, incluidos los operadores centrales para redactar consultas de forma monádica. [14]
- ZipperFS es un sistema de archivos simple y experimental que también utiliza la estructura de cremallera principalmente para implementar sus funciones. [15]
- El marco de las extensiones reactivas esencialmente proporciona una interfaz (co) monádica para los flujos de datos que realiza el patrón del observador . [dieciséis]
Historia
El término "mónada" en la programación en realidad se remonta a los lenguajes de programación APL y J , que tienden a ser puramente funcionales. Sin embargo, en esos lenguajes, "mónada" es sólo una abreviatura de una función que toma un parámetro (una función con dos parámetros es una "díada", y así sucesivamente). [17]
El matemático Roger Godement fue el primero en formular el concepto de mónada (denominándolo una "construcción estándar") a finales de la década de 1950, aunque el término "mónada" que llegó a dominar fue popularizado por el teórico de categorías Saunders Mac Lane . [ cita requerida ] La forma definida anteriormente usando bind , sin embargo, fue descrita originalmente en 1965 por el matemático Heinrich Kleisli para demostrar que cualquier mónada podría caracterizarse como un adjunto entre dos functores (covariantes). [18]
A partir de la década de 1980, una vaga noción del patrón de mónadas comenzó a surgir en la comunidad de las ciencias de la computación. Según el investigador de lenguajes de programación Philip Wadler , el científico informático John C. Reynolds anticipó varias facetas de la misma en la década de 1970 y principios de la de 1980, cuando discutió el valor del estilo de continuación de paso , la teoría de categorías como una fuente rica para la semántica formal y el tipo distinción entre valores y cálculos. [4] El lenguaje de investigación Opal , que se diseñó activamente hasta 1990, también basó eficazmente la E / S en un tipo monádico, pero la conexión no se realizó en ese momento. [19]
El científico informático Eugenio Moggi fue el primero en vincular explícitamente la mónada de la teoría de categorías a la programación funcional, en un artículo de conferencia en 1989, [20] seguido de una publicación de revista más refinada en 1991. En trabajos anteriores, varios científicos informáticos habían avanzado utilizando teoría de categorías para proporcionar semántica para el cálculo lambda . La idea clave de Moggi fue que un programa del mundo real no es solo una función de valores a otros valores, sino más bien una transformación que forma cálculos sobre esos valores. Cuando se formaliza en términos de teoría de categorías, esto lleva a la conclusión de que las mónadas son la estructura para representar estos cálculos. [3]
Varios otros popularizaron y desarrollaron esta idea, incluidos Philip Wadler y Simon Peyton Jones , ambos involucrados en la especificación de Haskell. En particular, Haskell utilizó un modelo problemático de "flujo diferido" hasta la versión 1.2 para conciliar la E / S con la evaluación diferida , hasta cambiar a una interfaz monádica más flexible. [21] La comunidad Haskell continuaría aplicando mónadas a muchos problemas en la programación funcional, y en la década de 2010, los investigadores que trabajaban con Haskell finalmente reconocieron cómo las mónadas se relacionan con otras estructuras como functores aplicativos y flechas . [ cita requerida ]
Al principio, la programación con mónadas se limitaba en gran medida a Haskell y sus derivados, pero como la programación funcional ha influido en otros paradigmas, muchos lenguajes han incorporado un patrón de mónadas (en espíritu, si no en nombre). Las formulaciones ahora existen en Scheme , Perl , Python , Racket , Clojure , Scala , F # , y también se han considerado para un nuevo estándar ML . [ cita requerida ]
Análisis
Uno de los beneficios del patrón de mónadas es que aporta precisión matemática a la lógica del programa. No solo se pueden usar las leyes de las mónadas para verificar la validez de una instancia, sino que también se pueden usar características de estructuras relacionadas (como functores) mediante subtipificación .
Verificando las leyes de las mónadas
Volviendo al Maybe
ejemplo, se declaró que sus componentes formaban una mónada, pero no se dio ninguna prueba de que satisfaga las leyes de la mónada.
Esto se puede rectificar conectando los detalles de Maybe
en un lado de las leyes generales, luego construyendo algebraicamente una cadena de igualdades para llegar al otro lado:
Ley 1: eta (a) >> = f (x) ⇔ (Solo a) >> = f (x) ⇔ f (a)
Ley 2: ma >> = eta (x) ⇔ ma si ma es (solo a) entonces eta (a) ⇔ Solo un si no o Nada ⇔ Nada terminara si
Ley 3: ( ma >> = f (x) ) >> = g (y) ⇔ ma >> = ( f (x) >> = g (y) ) si (ma >> = f (x)) es (Solo b) entonces si ma es (Solo a) entonces g (ma >> = f (x)) (f (x) >> = g (y)) a más más Nada nada terminar si terminar si ⇔ si ma es (Just a) y f (a) es (Just b) a continuación, (g ∘ f) a else if ma es (Justo a) y f (a) es nada, entonces Nada demás Nada terminara si
Derivación de functores
Aunque es más raro en ciencias de la computación, se puede usar la teoría de categorías directamente, que define una mónada como un funtor con dos transformaciones naturales adicionales . Entonces, para comenzar, una estructura requiere una función de orden superior (o "funcional") llamada map para calificar como un funtor:
map φ : (a → b) → (ma → mb)
Sin embargo, esto no siempre es un problema importante, especialmente cuando una mónada se deriva de un funtor preexistente, después de lo cual la mónada hereda el mapa automáticamente. (Por razones históricas, esto map
se llama fmap
en cambio en Haskell).
La primera transformación de una mónada es en realidad la misma unidad del triple de Kleisli, pero siguiendo de cerca la jerarquía de estructuras, resulta que la unidad caracteriza un funtor aplicativo , una estructura intermedia entre una mónada y un funtor básico. En el contexto aplicativo, a veces se hace referencia a la unidad como pura, pero sigue siendo la misma función. Lo que difiere en esta construcción es la ley que debe satisfacer la unidad ; como bind no está definido, la restricción se da en términos de mapa en su lugar:
(unit ∘ φ) x ↔ ((map φ) ∘ unit) x
[22]El salto final del functor aplicativo a la mónada viene con la segunda transformación, la función de unión (en la teoría de categorías, esta es una transformación natural generalmente llamada μ ), que "aplana" las aplicaciones anidadas de la mónada:
join(mma) : M (M T) → M T
Como función característica, join también debe satisfacer tres variaciones de las leyes de las mónadas: [ cita requerida ]
join ∘ (map join) mmma ↔ (join ∘ join) mmma ↔ ma
join ∘ (map unit) ma ↔ (join ∘ unit) ma ↔ ma
join ∘ (map map φ) mma ↔ ((map φ) ∘ join) mma ↔ mb
Independientemente de si un desarrollador define una mónada directa o un triple de Kleisli, la estructura subyacente será la misma y las formas se pueden derivar entre sí fácilmente:
(map φ) ma ↔ ma >>= (unit ∘ φ)
join(mma) ↔ mma >>= id
ma >>= f ↔ (join ∘ (map f)) ma
[23]Otro ejemplo: List
La mónada List demuestra naturalmente cómo puede resultar útil derivar una mónada a partir de un funtor más simple. En muchos lenguajes, una estructura de lista viene predefinida junto con algunas características básicas, por lo que un List
constructor de tipo y un operador de adición (representados con ++
notación infija) se asumen como ya se han dado aquí.
Incrustar un valor simple en una lista también es trivial en la mayoría de los idiomas:
unidad (x) = [x]
A partir de aquí, aplicar una función de forma iterativa con una comprensión de lista puede parecer una opción fácil para vincular y convertir listas en una mónada completa. La dificultad con este enfoque es que bind espera funciones monádicas, que en este caso generarán listas por sí mismas; a medida que se apliquen más funciones, se acumularán capas de listas anidadas, que requerirán más que una comprensión básica.
Sin embargo, un procedimiento para aplicar cualquier función simple en toda la lista, en otras palabras map , es sencillo:
(mapa φ) xlist = [φ (x1), φ (x2), ..., φ (xn)]
Ahora bien, estos dos procedimientos ya promueven List
a un funtor aplicativo. Para calificar completamente como una mónada, solo se necesita una noción correcta de unión para aplanar la estructura repetida, pero para las listas, eso solo significa desenvolver una lista externa para agregar las internas que contienen valores:
unirse (xlistlist) = unirse ([xlist1, xlist2, ..., xlistn]) = xlist1 ++ xlist2 ++ ... ++ xlistn
La mónada resultante no es solo una lista, sino una que automáticamente cambia de tamaño y se condensa a medida que se aplican las funciones. bind ahora también se puede derivar con solo una fórmula, luego se usa para alimentar List
valores a través de una tubería de funciones monádicas:
(xlist >> = f) = unirse ∘ (mapa f) xlist
Una aplicación para esta lista monádica es la representación de cálculos no deterministas . List
puede contener resultados para todas las rutas de ejecución en un algoritmo, luego condensarse en cada paso para "olvidar" qué rutas llevaron a qué resultados (una distinción a veces importante de los algoritmos deterministas y exhaustivos). [ cita requerida ] Otro beneficio es que los cheques se pueden incrustar en la mónada; Las rutas específicas se pueden podar de forma transparente en su primer punto de falla, sin necesidad de reescribir funciones en la canalización. [23]
Una segunda situación en la que List
brilla es la de componer funciones multivalor . Por ejemplo, el n º raíz compleja de un número debe dió n números complejos distintos, pero si otro m se toma entonces º raíz de estos resultados, la final m • n valores debe ser idéntica a la salida del m • n º raíz . List
automatiza completamente este problema, condensando los resultados de cada paso en una lista plana y matemáticamente correcta. [24]
Técnicas
Las mónadas presentan oportunidades para técnicas interesantes más allá de la simple organización de la lógica del programa. Las mónadas pueden sentar las bases para características sintácticas útiles, mientras que su naturaleza matemática y de alto nivel permite una abstracción significativa.
Azúcar sintáctica notación
Aunque usar bind abiertamente a menudo tiene sentido, muchos programadores prefieren una sintaxis que imite declaraciones imperativas (llamada notación do en Haskell, notación de ejecución en OCaml , expresiones de cálculo en F # , [25] y para comprensión en Scala ). Esto es solo azúcar sintáctico que disfraza una tubería monádica como un bloque de código ; A continuación, el compilador traducirá silenciosamente estas expresiones al código funcional subyacente.
Traducir la add
función de Maybe
Haskell a Haskell puede mostrar esta característica en acción. Una versión no monádica de add
en Haskell se ve así:
agregar mx my = caso mx de Nada -> Nada Solo x -> caso mi de Nada -> Nada Solo y -> Solo ( x + y )
En Haskell monádico, return
es el nombre estándar para la unidad , además las expresiones lambda deben manejarse explícitamente, pero incluso con estos tecnicismos, la Maybe
mónada ofrece una definición más limpia:
sumar mx my = mx >> = ( \ x -> my >> = ( \ y -> return ( x + y )))
Sin embargo, con la notación do, esto se puede destilar aún más en una secuencia muy intuitiva:
sumar mx my = do x <- mx y <- my return ( x + y )
Un segundo ejemplo muestra cómo Maybe
se puede usar en un idioma completamente diferente: F #. Con las expresiones de cálculo, una función de "división segura" que devuelve None
un operando indefinido o una división por cero se puede escribir como:
let readNum () = let s = Console . ReadLine () sea succ , v = Int32 . TryParse ( s ) if ( succ ) then Some ( v ) else Nonelet secure_div = tal vez { let ! x = readNum () let ! y = readNum () if ( y = 0 ) then None else return ( x / y ) }
En el momento de la compilación, el compilador "eliminará el azúcar" internamente de esta función en una cadena más densa de llamadas de enlace :
tal vez . Delay ( fun () -> maybe . Bind ( readNum () , fun x -> maybe . Bind ( readNum () , fun y -> if ( y = 0 ) then None else maybe . Return ( x / y ))) )
Para un último ejemplo, incluso las leyes generales de las mónadas pueden expresarse en notación do:
hacer { x <- devolver v ; f x } == hacer { f v } hacer { x <- m ; return x } == hacer { m } hacer { y <- hacer { x <- m ; f x }; g y } == hacer { x <- m ; y <- f x ; g y }
Si bien es conveniente, un desarrollador siempre debe recordar que este estilo de bloque es puramente sintáctico y se puede reemplazar con expresiones aparentemente monádicas (o incluso no monádicas CPS). El uso de bind para expresar la canalización monádica puede ser aún más claro en muchos casos, y algunos defensores de la programación funcional incluso argumentan que, dado que el estilo de bloque permite a los principiantes trasladar hábitos de la programación imperativa, debe evitarse de forma predeterminada y usarse solo cuando sea obviamente superior. [26] [1]
Interfaz general
Cada mónada necesita una implementación específica que cumpla con las leyes de las mónadas, pero otros aspectos como la relación con otras estructuras o modismos estándar dentro de una lengua son compartidos por todas las mónadas. Como resultado, un idioma o biblioteca puede proporcionar una Monad
interfaz general con prototipos de funciones , relaciones de subtipificación y otros hechos generales. Además de proporcionar una ventaja para el desarrollo y garantizar que una nueva mónada herede características de un supertipo (como functores), comparar el diseño de una mónada con la interfaz agrega otra capa de control de calidad. [ cita requerida ]
Operadores
El código monádico a menudo se puede simplificar aún más mediante el uso juicioso de operadores. El mapa funcional puede ser especialmente útil ya que funciona en más que funciones monádicas ad-hoc; siempre que una función monádica funcione de manera análoga a un operador predefinido, el mapa se puede utilizar para " elevar " instantáneamente al operador más simple a uno monádico. [f] Con esta técnica, la definición de add
del Maybe
ejemplo podría destilarse en:
agregar (mx, mi) = mapa (+)
El proceso podría llevarse incluso un paso más allá definiendo add
no solo para Maybe
, sino para toda la Monad
interfaz. Al hacer esto, cualquier nueva mónada que coincida con la interfaz de estructura e implemente su propio mapa heredará inmediatamente una versión mejorada de add
también. El único cambio necesario en la función es generalizar la firma de tipo:
añadir: (Número de mónada, Número de mónada) → Número de mónada [27]
Otro operador monádico que también es útil para el análisis es la composición monádica (representada >=>
aquí como infijo ), que permite encadenar funciones monádicas en un estilo más matemático:
(f> => g) x = (f (x) → mb) >> = g (y = b)
Con este operador, las leyes de las mónadas se pueden escribir solo en términos de funciones, destacando la correspondencia con la asociatividad y la existencia de una identidad:
(unidad> => g) ↔ g(f> => unidad) ↔ f(f> => g)> => h ↔ f> => (g> => h) [1]
Variaciones
A nivel matemático, algunas mónadas tienen propiedades particularmente agradables y se adaptan de manera única a ciertos problemas.
Mónadas aditivas
Una mónada aditiva es una mónada dotada de un operador binario asociativo cerrado adicional mplus y un elemento de identidad bajo mplus , llamado mzero . La Maybe
mónada puede considerarse aditivo, con Nothing
como mzero y una variación en el OR operador como mplus . List
también es una mónada aditiva, con la lista vacía []
actuando como mzero y el operador de concatenación ++
como mplus .
Intuitivamente, mzero representa una envoltura monádica sin valor de un tipo subyacente, pero también se considera un "cero" (en lugar de un "uno") ya que actúa como un absorbedor para vincular , devolviendo mzero siempre que esté vinculado a una función monádica. Esta propiedad es de dos caras y bind también devolverá mzero cuando cualquier valor esté vinculado a una función cero monádica .
En términos de teoría de categorías, una mónada aditiva califica una vez como un monoide sobre funciones monádicas con bind (como lo hacen todas las mónadas), y nuevamente sobre valores monádicos a través de mplus . [28] [g]
Mónadas libres
A veces, el esquema general de una mónada puede ser útil, pero ningún patrón simple recomienda una mónada u otra. Aquí es donde entra una mónada libre ; como objeto libre en la categoría de mónadas, puede representar una estructura monádica sin restricciones específicas más allá de las propias leyes de las mónadas. Así como un monoide libre concatena elementos sin evaluación, una mónada libre permite encadenar cálculos con marcadores para satisfacer el sistema de tipos, pero por lo demás no impone una semántica más profunda.
Por ejemplo, al trabajar completamente a través de los marcadores Just
y Nothing
, la Maybe
mónada es de hecho una mónada libre. La List
mónada, por otro lado, no es una mónada libre ya que aporta datos adicionales y específicos sobre listas (como anexar ) a su definición. Un último ejemplo es una mónada libre abstracta, que usa marcadores de tipo para unit y bind y un constructor de tipo lineal recursivo:
newtype Free F (T) = Unidad T o Bind (F, Free F (T))unidad (x) = Unidad xmx >> = f = ... si mx es la Unidad x entonces ... f (x) demás ... Enlazar (f, mx) terminara si
Las mónadas libres, sin embargo, no están restringidas a una lista enlazada como en este ejemplo, y pueden construirse alrededor de otras estructuras como árboles .
El uso de mónadas libres intencionalmente puede parecer poco práctico al principio, pero su naturaleza formal es particularmente adecuada para problemas sintácticos. Se puede usar una mónada libre para rastrear la sintaxis y el tipo, dejando la semántica para más adelante y, como resultado, ha encontrado uso en analizadores e intérpretes . [29] Otros también los han aplicado a problemas operativos más dinámicos, como proporcionar iteraciones dentro de un idioma. [30]
Comonads
Además de generar mónadas con propiedades adicionales, para cualquier mónada dada, también se puede definir una comónada . Conceptualmente, si las mónadas representan cálculos construidos a partir de valores subyacentes, entonces las comónadas pueden verse como reducciones a valores. El código monádico, en cierto sentido, no se puede "desempaquetar" por completo; una vez que un valor se envuelve dentro de una mónada, permanece en cuarentena allí junto con los efectos secundarios (algo bueno en la programación puramente funcional). A veces, sin embargo, un problema se trata más de consumir datos contextuales, que las comonads pueden modelar explícitamente.
Técnicamente, una comónada es el dual categórico de una mónada, lo que significa vagamente que tendrá los mismos componentes requeridos, solo que con la dirección de las firmas de tipo invertida . A partir de la definición de mónada centrada en la unión , una comónada consta de:
- Un constructor de tipos W que marca el tipo de orden superior WT
- El dual de unidad , llamado contador aquí, extrae el valor subyacente del comonad:
cuenta (wa): WT → T
- Una inversión de enlace (también representada con
=>>
) que extiende s una cadena de funciones reductoras:
(wa = >> f): (WU, WU → T) → WT [h]
extender y contar también deben satisfacer duales de las leyes de la mónada:
cuenta ∘ ( (wa = >> f) → wb ) ↔ f (wa) → bwa = >> cuenta ↔ wawa ( (= >> f (wx = wa)) → wb (= >> g (wy = wb)) → wc ) ↔ ( wa (= >> f (wx = wa)) → wb ) (= >> g (wy = wb)) → wc
De manera análoga a las mónadas, las comónadas también se pueden derivar de functores usando un dual de join :
- duplicate toma un valor ya comonádico y lo envuelve en otra capa de estructura comonádica:
duplicado (wa): WT → W (WT)
Mientras que las operaciones como extender se invierten, sin embargo, una comónada no invierte las funciones sobre las que actúa y, en consecuencia, las comónadas siguen siendo functores con map , no cofunctores . La definición alternativa con duplicado , recuento y mapa también debe respetar sus propias leyes de comonad:
((mapa duplicado) ∘ duplicado) wa ↔ (duplicado ∘ duplicado) wa ↔ wwwa((recuento de mapas) duplicado) wa ↔ (recuento ∘ duplicado) wa ↔ wa((mapa mapa φ) ∘ duplicado) wa ↔ (duplicado ∘ (mapa φ)) wa ↔ wwb
Y al igual que con las mónadas, las dos formas se pueden convertir automáticamente:
(mapa φ) wa ↔ wa = >> (φ ∘ cuenta) wxduplicar wa ↔ wa = >> wx
wa = >> f (wx) ↔ ((mapa f) ∘ duplicar) wa
Un ejemplo simple es el producto comonad , que genera valores basados en un valor de entrada y datos ambientales compartidos. De hecho, la Product
comónada es simplemente el dual de la Writer
mónada y efectivamente es lo mismo que la Reader
mónada (ambos discutidos a continuación). Product
y Reader
difieren solo en qué firmas de funciones aceptan y cómo complementan esas funciones al envolver o desenvolver valores.
Un ejemplo menos trivial es el comando Stream , que se puede utilizar para representar flujos de datos y adjuntar filtros a las señales entrantes con extender . De hecho, aunque no son tan populares como las mónadas, los investigadores han encontrado que las comónadas son particularmente útiles para el procesamiento de flujos y la programación de flujos de datos de modelado . [31] [32]
Sin embargo, debido a sus definiciones estrictas, uno no puede simplemente mover objetos de un lado a otro entre mónadas y comónadas. Como una abstracción aún mayor, las flechas pueden subsumir ambas estructuras, pero encontrar formas más granulares de combinar código monádico y comonádico es un área activa de investigación. [33] [34]
Más ejemplos
Mónada de identidad
La mónada más simple es la mónada de identidad , que simplemente anota valores y funciones simples para satisfacer las leyes de la mónada:
newtype Id T = Tunidad (x) = x(x >> = f) = f (x)
Identity
Sin embargo, en realidad tiene usos válidos, como proporcionar un caso base para transformadores de mónada recursivos . También se puede utilizar para realizar asignaciones de variables básicas dentro de un bloque de estilo imperativo. [i] [ cita requerida ]
Colecciones
Cualquier colección con un anexo adecuado ya es un monoide gratuito, pero resulta que List
no es la única colección que también tiene una combinación bien definida y califica como una mónada. Incluso se puede mutar List
en estas otras colecciones monádicas simplemente imponiendo propiedades especiales al anexo : [j] [ cita requerida ]
Colección | Propiedades monoide |
---|---|
Lista | Libre |
Multiset finito | Conmutativa |
Conjunto finito | Conmutativo e idempotente |
Permutaciones finitas | No conmutativo e idempotente |
IO mónada (Haskell)
Como ya se mencionó, el código puro no debería tener efectos secundarios no administrados, pero eso no impide que un programa describa y administre explícitamente los efectos. Esta idea es fundamental para la mónada IO de Haskell , donde IO a
se puede considerar que un objeto de tipo contiene el estado actual del mundo fuera del programa y calcula un valor de tipo a
. Un cálculo que no calcula ningún valor, es decir, un procedimiento, tiene el tipo IO ()
"calcular" el valor ficticio ()
. Cuando un programador vincula un IO
valor a una función, la función toma decisiones basadas en esa visión del mundo (entrada de usuarios, archivos, etc.), luego produce un valor monádico que refleja el nuevo estado mundial (salida del programa). [21]
Por ejemplo, Haskell tiene varias funciones para actuar en el sistema de archivos más amplio , incluida una que verifica si existe un archivo y otra que elimina un archivo. Sus dos firmas de tipo son:
doesFileExist :: FilePath -> IO Bool removeFile :: FilePath -> IO ()
El primero está interesado en si un archivo dado realmente existe y, como resultado, genera un valor booleano dentro de la IO
mónada. La segunda función, por otro lado, solo se ocupa de actuar en el sistema de archivos, por lo que el IO
contenedor que genera está vacío.
IO
Sin embargo, no se limita solo a E / S de archivos; incluso permite la E / S del usuario y, junto con el azúcar de sintaxis imperativa, puede imitar un típico "¡Hola, mundo!" programa :
main :: IO () main = do putStrLn "¡Hola, mundo!" putStrLn "¿Cuál es su nombre, usuario?" name <- getLine putStrLn ( "Encantado de conocerte, ++ nombre ++ "! " )
Desaprovechado, esto se traduce en la siguiente canalización monádica ( >>
en Haskell es solo una variante de enlace para cuando solo importan los efectos monádicos y el resultado subyacente puede descartarse):
main :: IO () main = putStrLn "¡Hola, mundo!" >> putStrLn "¿Cuál es su nombre, usuario?" >> getLine >> = ( \ nombre -> putStrLn ( "Encantado de conocerte," ++ nombre ++ "!" ))
Mónada del escritor (JavaScript)
Otra situación común es mantener un archivo de registro o informar sobre el progreso de un programa. A veces, un programador puede querer registrar datos técnicos aún más específicos para su posterior creación de perfiles o depuración . La mónada Writer puede manejar estas tareas generando una salida auxiliar que se acumula paso a paso.
Para mostrar cómo el patrón de mónada no está restringido a lenguajes principalmente funcionales, este ejemplo implementa una Writer
mónada en JavaScript . Primero, una matriz (con colas anidadas) permite construir el Writer
tipo como una lista vinculada . El valor de salida subyacente vivirá en la posición 0 de la matriz, y la posición 1 tendrá implícitamente una cadena de notas auxiliares:
escritor constante = valor => [ valor , []];
Definir la unidad también es muy simple:
unidad constante = valor => [ valor , []];
Solo se necesita unidad para definir funciones simples que generan Writer
objetos con notas de depuración:
const al cuadrado = x => [ x * x , [ ` $ { x } fue al cuadrado. ' ]]; const reducido a la mitad = x => [ x / 2 , [ ` $ { x } se redujo a la mitad` ]];
Una mónada verdadera todavía requiere un enlace , pero Writer
esto equivale simplemente a agregar la salida de una función a la lista enlazada de la mónada:
const bind = ( escritor , transformar ) => { const [ valor , registro ] = escritor ; const [ resultado , actualizaciones ] = transformar ( valor ); return [ resultado , registro . concat ( actualizaciones )]; };
Las funciones de muestra ahora se pueden encadenar juntas usando bind , pero definir una versión de composición monádica (llamada pipelog
aquí) permite aplicar estas funciones de manera aún más sucinta:
const pipelog = ( escritor , ... transforma ) => transforma . reducir ( enlazar , escritor );
El resultado final es una clara separación de preocupaciones entre recorrer los cálculos y registrarlos para auditarlos más tarde:
pipelog ( unidad ( 4 ), cuadrado , partido a la mitad ); // Objeto de escritor resultante = [8, ['4 fue al cuadrado.', '16 se redujo a la mitad. ']]
Mónada ambiental
Una mónada de entorno (también llamada mónada de lectura y mónada de función ) permite que un cálculo dependa de valores de un entorno compartido. El constructor de tipo mónada asigna un tipo T a funciones de tipo E → T , donde E es el tipo de entorno compartido. Las funciones de la mónada son:
Las siguientes operaciones monádicas son útiles:
La operación ask se utiliza para recuperar el contexto actual, mientras que local ejecuta un cálculo en un subcontexto modificado. Como en una mónada de estado, los cálculos en la mónada de entorno pueden invocarse simplemente proporcionando un valor de entorno y aplicándolo a una instancia de la mónada.
Formalmente, un valor en una mónada de entorno es equivalente a una función con un argumento anónimo adicional; return y bind son equivalentes a los combinadores K y S , respectivamente, en el cálculo del combinador SKI .
Mónadas estatales
Una mónada de estado permite a un programador adjuntar información de estado de cualquier tipo a un cálculo. Dado cualquier tipo de valor, el tipo correspondiente en la mónada de estado es una función que acepta un estado, luego genera un nuevo estado (de tipo s ) junto con un valor de retorno (de tipo t ). Esto es similar a una mónada de entorno, excepto que también devuelve un nuevo estado y, por lo tanto, permite modelar un entorno mutable .
tipo Estado s t = s -> ( t , s )
Tenga en cuenta que esta mónada toma un parámetro de tipo, el tipo de información de estado. Las operaciones de las mónadas se definen de la siguiente manera:
- "return" produce el valor dado sin cambiar el estado. return x = \ s -> ( x , s ) - "bind" modifica m para que aplique f a su resultado. m >> = f = \ r -> sea ( x , s ) = m r en ( f x ) s
Las operaciones estatales útiles incluyen:
get = \ s -> ( s , s ) : examina el estado en este punto del cálculo. put s = \ _ -> ( () , s ) - Reemplaza el estado. modificar f = \ s -> ( () , f s ) - Actualizar el estado
Otra operación aplica una mónada de estado a un estado inicial dado:
runState :: State s a -> s -> ( a , s ) runState t s = t s
Los bloques do en una mónada de estado son secuencias de operaciones que pueden examinar y actualizar los datos de estado.
De manera informal, una mónada de estado de tipo de estado S mapea el tipo de valores de retorno T en funciones de tipo, donde S es el estado subyacente. Las funciones de retorno y vinculación son:
- .
Desde el punto de vista de la teoría de categorías, una mónada de estado se deriva de la adjunción entre el functor producto y el functor exponencial, que existe en cualquier categoría cerrada cartesiana por definición.
Mónada de continuación
Una mónada de continuación con tipo de retorno R mapea el tipo T en funciones de tipo. Se utiliza para modelar el estilo de continuación de pases . Las funciones de retorno y vinculación son las siguientes:
La función de llamada con continuación de corriente se define de la siguiente manera:
Registro de programas
El siguiente código es un pseudocódigo. Supongamos que tenemos dos funciones foo
y bar
, con tipos
foo : int -> int bar : int -> int
Es decir, ambas funciones toman un entero y devuelven otro entero. Entonces podemos aplicar las funciones en sucesión así:
foo ( barra x )
Donde el resultado es el resultado de foo
aplicado al resultado de bar
aplicado a x
.
Pero supongamos que estamos depurando nuestro programa y nos gustaría agregar mensajes de registro a foo
y bar
. Entonces cambiamos los tipos de la siguiente manera:
foo : int -> int * string bar : int -> int * string
De modo que ambas funciones devuelven una tupla, con el resultado de la aplicación como entero, y un mensaje de registro con información sobre la función aplicada y todas las funciones aplicadas previamente como cadena.
Desafortunadamente, esto significa que ya no podemos componer foo
y bar
, dado que su tipo de entrada int
no es compatible con su tipo de salida int * string
. Y aunque nuevamente podemos ganar componibilidad modificando los tipos de cada función int * string -> int * string
, esto requeriría que agreguemos código repetitivo a cada función para extraer el número entero de la tupla, lo que se volvería tedioso a medida que aumenta el número de tales funciones.
En su lugar, definamos una función auxiliar para abstraernos de este texto estándar:
enlazar : int * cadena -> ( int -> int * cadena ) -> int * cadena
bind
toma un entero y una tupla de cadena, luego toma una función (como foo
) que se asigna de un entero a un entero y una tupla de cadena. Su salida es un entero y una tupla de cadena, que es el resultado de aplicar la función de entrada al entero dentro del entero de entrada y la tupla de cadena. De esta manera, solo necesitamos escribir código repetitivo para extraer el entero de la tupla una vez, en formato bind
.
Ahora hemos recuperado algo de componibilidad. Por ejemplo:
enlazar ( enlazar ( x , s ) barra ) foo
Donde (x,s)
es una tupla de enteros y cadenas.
Para que los beneficios sean aún más claros, definamos un operador infijo como un alias para bind
:
( >> = ) : int * cadena -> ( int -> int * cadena ) -> int * cadena
Entonces eso t >>= f
es lo mismo que bind t f
.
Entonces el ejemplo anterior se convierte en:
(( x , s ) >> = barra ) >> = foo
Finalmente, sería bueno no tener que escribir (x, "")
cada vez que deseamos crear un mensaje de registro vacío, donde ""
está la cadena vacía. Así que definamos una nueva función:
retorno : int -> int * cadena
Que se envuelve x
en la tupla descrita anteriormente.
Ahora tenemos una buena canalización para registrar mensajes:
(( devuelve x ) >> = barra ) >> = foo
Que nos permite entrar más fácilmente los efectos de bar
y foo
sobre x
.
int * string
es análogo a un valor monádico . bind
y return
son análogas a las funciones correspondientes del mismo nombre. De hecho, int * string
, bind
, y return
formar una mónada.
Ver también
Alternativas para modelar cálculos:
- Los sistemas de efectos son una forma diferente de describir los efectos secundarios como tipos
- Los tipos de singularidad son un tercer enfoque para manejar los efectos secundarios en lenguajes funcionales
Conceptos de diseño relacionados:
- La programación orientada a aspectos enfatiza la separación del código de contabilidad auxiliar para mejorar la modularidad y la simplicidad
- La inversión del control es el principio abstracto de llamar a funciones específicas desde un marco general
- Las clases de tipos son una característica de lenguaje específica que se usa para implementar mónadas y otras estructuras en Haskell.
- El patrón de decorador es una forma más concreta y ad-hoc de lograr beneficios similares en la programación orientada a objetos.
Generalizaciones de mónadas:
- Los functores aplicativos se generalizan a partir de mónadas manteniendo solo la unidad y las leyes que la relacionan con el mapa.
- Las flechas usan una estructura adicional para traer funciones simples y mónadas bajo una sola interfaz
- Los transformadores de mónadas actúan sobre distintas mónadas para combinarlas de forma modular
Notas
- ^ Debido al hecho de que las funciones en múltiples variables libres son comunes en la programación, las mónadas como se describen en este artículo son técnicamente lo que los teóricos de categorías llamarían mónadas fuertes . [3]
- ^ Semánticamente, M no es trivial y representa un endofunctor sobre la categoría de todos los valores bien tipificados:
- ^ Mientras que una función (paramétricamente polimórfica) en términos de programación, la unidad (a menudo llamada η en la teoría de categorías) es matemáticamente una transformación natural , que se asigna entre functores :
- ^ bind , por otro lado, no es una transformación natural en la teoría de categorías, sino más bien una extensiónque eleva un mapeo (de valores a cálculos) en un morfismo entre cálculos:
- ^ Estrictamente hablando, bind puede no ser formalmente asociativo en todos los contextos porque corresponde a la aplicación dentro del cálculo lambda , no a las matemáticas. En el cálculo lambda riguroso, la evaluación de un enlace puede requerir primero envolver el término derecho (al enlazar dos valores monádicos) o el enlace en sí mismo (entre dos funciones monádicas) en una función anónima para aceptar la entrada de la izquierda. [8]
- ^ Algunos lenguajes como Haskell incluso proporcionan un seudónimo para el mapa en otros contextos llamado
lift
, junto con múltiples versiones para diferentes recuentos de parámetros, un detalle que se ignora aquí. - ^ Algebraicamente, la relación entre los dos aspectos monoide (no conmutativos) se asemeja a la de un semirrígido cercano , y algunas mónadas aditivas sí califican como tales. Sin embargo, no todas las mónadas aditivas cumplen lasleyes distributivas de incluso un semirrígido. [28]
- ^ En Haskell, extender se define en realidad con las entradas intercambiadas, pero como no se usa currying en este artículo, se define aquí como el dual exacto de bind .
- ^ En la teoría de categorías, la
Identity
mónada también puede verse como emergente de la adjunción de cualquier funtor con su inverso. - ^ La teoría de categorías considera estas mónadas de colección como adjuntos entre el functor libre y diferentes functores de la categoría de conjuntos a la categoría de monoides .
Referencias
- ↑ a b c d e f g h O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). "Mónadas" . Haskell del mundo real . Sebastopol, California: O'Reilly Media. capítulo 14. ISBN 978-0596514983.
- ^ a b Wadler, Philip (junio de 1990). Comprensión de las mónadas . Conferencia ACM sobre LISP y Programación Funcional. Linda, Francia. CiteSeerX 10.1.1.33.5381 .
- ^ a b c Moggi, Eugenio (1991). "Nociones de computación y mónadas" (PDF) . Información y Computación . 93 (1): 55–92. CiteSeerX 10.1.1.158.5275 . doi : 10.1016 / 0890-5401 (91) 90052-4 .
- ^ a b c d e Wadler, Philip (enero de 1992). La esencia de la programación funcional . XIX Simposio anual de ACM sobre principios de lenguajes de programación. Albuquerque, Nuevo México. CiteSeerX 10.1.1.38.9516 .
- ^ a b Hudak, Paul ; Peterson, John; Fasel, Joseph (1999). "Acerca de las mónadas" . Una suave introducción a Haskell 98 . Capítulo 9.
- ^ Respuesta de CA McCann (23 de julio de 2010 a las 23:39) ¿Cómo y por qué funciona la mónada Haskell Cont?
- ^ Spivey, Mike (1990). "Una teoría funcional de excepciones" (PDF) . Ciencia de la Programación de Computadores . 14 (1): 25–42. doi : 10.1016 / 0167-6423 (90) 90056-J .
- ^ "Leyes de las mónadas" . HaskellWiki . haskell.org . Consultado el 14 de octubre de 2018 .
- ^ "Lo que no es una mónada" . 7 de octubre de 2018.
- ^ De Meuter, Wolfgang (1997). Mónadas como fundamento teórico para AOP (PDF) . Taller Internacional de Programación Orientada a Aspectos en ECOOP. Jyväskylä, Finlandia. CiteSeerX 10.1.1.25.8262 .
- ^ "Mónada (sin metáforas)" . HaskellWiki . 1 de noviembre de 2009 . Consultado el 24 de octubre de 2018 .
- ^ O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). "Usando Parsec" . Haskell del mundo real . Sebastopol, California: O'Reilly Media. capítulo 16. ISBN 978-0596514983.
- ^ Stewart, Don (17 de mayo de 2007). "Roll Your Own Window Manager: seguimiento del enfoque con una cremallera" . Control.Monad.Writer . Archivado desde el original el 20 de febrero de 2018 . Consultado el 19 de noviembre de 2018 .
- ^ Benton, Nick (2015). "Mónadas categóricas y programación informática" (PDF) . London Mathematical Society Impact150 Stories . 1 . Consultado el 19 de noviembre de 2018 .
- ^ Kiselyov, Olag (2007). "Continuaciones delimitadas en sistemas operativos". Modelado y uso del contexto . Apuntes de conferencias en Ciencias de la Computación. Springer Berlín Heidelberg. 4635 . páginas 291-302. doi : 10.1007 / 978-3-540-74255-5_22 . ISBN 978-3-540-74255-5.
- ^ Meijer, Erik (27 de marzo de 2012). "Tu ratón es una base de datos" . Cola de ACM . 10 (3): 20–33. doi : 10.1145 / 2168796.2169076 . Consultado el 19 de noviembre de 2018 .
- ^ Iverson, Kenneth (septiembre de 1987). "Un diccionario de APL" . APL Quote Quote . 18 (1): 5–40. doi : 10.1145 / 36983.36984 . ISSN 1088-6826 . Consultado el 19 de noviembre de 2018 .
- ^ Kleisli, Heinrich (1965). "Cada construcción estándar es inducida por un par de functores adjuntos" (PDF) . Actas de la American Mathematical Society . 16 (3): 544–546. doi : 10.1090 / S0002-9939-1965-0177024-4 . Consultado el 19 de noviembre de 2018 .
- ^ Peter Pepper, ed. (Noviembre de 1997). El lenguaje de programación Opal (informe técnico) (5ª edición corregida). Fachbereich Informatik, Technische Universität Berlin. CiteSeerX 10.1.1.40.2748 .
- ^ Moggi, Eugenio (junio de 1989). Mónadas y cálculo lambda computacional (PDF) . IV Simposio Anual de Lógica en Informática. Pacific Grove, California. CiteSeerX 10.1.1.26.2787 .
- ^ a b Peyton Jones, Simon L .; Wadler, Philip (enero de 1993). Programación funcional imperativa (PDF) . 20º Simposio anual de ACM sobre principios de lenguajes de programación. Charleston, Carolina del Sur. CiteSeerX 10.1.1.53.2504 .
- ^ "Functor aplicativo" . HaskellWiki . Haskell.org. 7 de mayo de 2018. Archivado desde el original el 30 de octubre de 2018 . Consultado el 20 de noviembre de 2018 .
- ^ a b Gibbard, Cale (30 de diciembre de 2011). "Mónadas como contenedores" . HaskellWiki . Haskell.org. Archivado desde el original el 14 de diciembre de 2017 . Consultado el 20 de noviembre de 2018 .
- ^ a b Piponi, Dan (7 de agosto de 2006). "¡Podrías haber inventado las mónadas! (Y quizás ya lo hayas hecho)" . Un barrio del infinito . Archivado desde el original el 24 de octubre de 2018 . Consultado el 16 de octubre de 2018 .
- ^ "Algunos detalles sobre las expresiones de cálculo F #" . Consultado el 9 de octubre de 2018 .
- ^ "No se considera una notación perjudicial" . HaskellWiki . Consultado el 12 de octubre de 2018 .
- ^ Giles, Brett (12 de agosto de 2013). "Levantamiento" . HaskellWiki . Haskell.org. Archivado desde el original el 29 de enero de 2018 . Consultado el 25 de noviembre de 2018 .
- ^ a b Rivas, Exequiel; Jaskelioff, Mauro; Schrijvers, Tom (julio de 2015). De los monoides a los semirríos: la esencia de MonadPlus y Alternative (PDF) . XVII Simposio Internacional ACM sobre Principios y Práctica de la Programación Declarativa. Siena, Italia. CiteSeerX 10.1.1.703.342 .
- ^ Swierstra, Wouter (2008). "Tipos de datos a la carta" (PDF) . Perla funcional. Revista de programación funcional . Prensa de la Universidad de Cambridge. 18 (4): 423–436. CiteSeerX 10.1.1.101.4131 . doi : 10.1017 / s0956796808006758 . ISSN 1469-7653 .
- ^ Kiselyov, Oleg (mayo de 2012). Schrijvers, Tom; Thiemann, Peter (eds.). Iteratees (PDF) . Simposio Internacional de Programación Lógica y Funcional. Apuntes de conferencias en Ciencias de la Computación. 7294 . Kobe, Japón: Springer-Verlag. págs. 166-181. doi : 10.1007 / 978-3-642-29822-6_15 . ISBN 978-3-642-29822-6.
- ^ Uustalu, Tarmo; Vene, Varmo (julio de 2005). Horváth, Zoltán (ed.). La esencia de la programación de flujo de datos (PDF) . Primera Escuela de Verano, Programación Funcional de Europa Central. Apuntes de conferencias en Ciencias de la Computación. 4164 . Budapest, Hungría: Springer-Verlag. págs. 135-167. CiteSeerX 10.1.1.62.2047 . ISBN 978-3-540-46845-5.
- ^ Uustalu, Tarmo; Vene, Varmo (junio de 2008). "Nociones comonádicas de computación" . Notas electrónicas en informática teórica . Elsevier. 203 (5): 263–284. doi : 10.1016 / j.entcs.2008.05.029 . ISSN 1571-0661 .
- ^ Poder, John; Watanabe, Hiroshi (mayo de 2002). "Combinando una mónada y una comónada" (PDF) . Informática Teórica . Elsevier. 280 (1-2): 137-162. CiteSeerX 10.1.1.35.4130 . doi : 10.1016 / s0304-3975 (01) 00024-x . ISSN 0304-3975 .
- ^ Gaboardi, Marco; Katsumata, Shin-ya; Huerto, Dominic; Breuvart, Flavien; Uustalu, Tarmo (septiembre de 2016). Combinación de efectos y coefectos mediante la clasificación (PDF) . 21ª Conferencia Internacional ACM sobre Programación Funcional. Nara, Japón: Asociación de Maquinaria de Computación. págs. 476–489. doi : 10.1145 / 2951913.2951939 . ISBN 978-1-4503-4219-3.
enlaces externos
Referencias de HaskellWiki:
- " Todo sobre las mónadas " (originalmente de Jeff Newbern): una discusión exhaustiva de todas las mónadas comunes y cómo funcionan en Haskell; incluye la analogía de la "línea de montaje mecanizada".
- " Typeclassopedia " (originalmente por Brent Yorgey) - Una exposición detallada de cómo se interrelacionan las principales clases de tipos de Haskell, incluidas las mónadas.
Tutoriales:
- " A Fistful of Monads " (del libro de texto de Haskell en línea Learn You a Haskell for Great Good! - Un capítulo que presenta las mónadas desde el punto de partida de las clases de tipos de functor y functor aplicativo, incluidos ejemplos.
- " Por unas mónadas más ": un segundo capítulo que explica más detalles y ejemplos, incluida una
Probability
mónada para las cadenas de Markov . - " Functores, aplicativos y mónadas en imágenes (por Aditya Bhargava): un tutorial rápido, gracioso y visual sobre las mónadas.
Casos interesantes:
- " Tubos UNIX como mónadas IO " (por Oleg Kiselyov): un breve ensayo que explica cómo los tubos Unix son efectivamente monádicos.
- Pro Scala: Monadic Design Patterns for the Web (por Gregory Meredith): un manuscrito completo inédito sobre cómo mejorar muchas facetas del desarrollo web en Scala con mónadas.