stringtranslate.com

Mónada (programación funcional)

En programación funcional , una mónada es una estructura que combina fragmentos de programa ( funciones ) y envuelve sus valores de retorno en un tipo con cálculo adicional. Además de definir un tipo monádico envolvente , las mónadas definen dos operadores : uno para envolver un valor en el tipo mónada y otro para componer funciones que generan valores del tipo mónada (éstas se conocen como funciones monádicas ). Los lenguajes de propósito general utilizan mónadas para reducir el código repetitivo necesario para operaciones comunes (como tratar con valores indefinidos o funciones falibles, o encapsular código de contabilidad). Los lenguajes funcionales utilizan mónadas para convertir secuencias complicadas de funciones en canales concisos que abstraen el flujo de control y 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 finales de los 80 y principios de los 90 establecieron que las mónadas podían resolver 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 mónadas , que deben ser satisfechos por cualquier mónada y pueden usarse para verificar el código monádico. [3] [4]

Dado que las mónadas hacen explícita la semántica para un tipo de cálculo, también pueden usarse para implementar funciones lingüísticas convenientes. Algunos lenguajes, como Haskell , incluso ofrecen definiciones prediseñadas en sus bibliotecas principales para la estructura general de mónadas y las instancias comunes. [ 15]

Descripción general

"Para una mónada m, un valor de tipo m arepresenta tener acceso a un valor de tipo adentro del contexto de la mónada". —CA McCann [6]

Más exactamente, se puede utilizar una mónada cuando el acceso sin restricciones a un valor no es apropiado por motivos específicos del escenario. En el caso de la mónada Maybe es porque es posible que el valor no exista. En el caso de la mónada IO, se debe a que es posible que aún no se conozca el valor, como cuando la mónada representa una entrada del usuario que solo se proporcionará después de que se muestre un mensaje. En todos los casos, los escenarios en los que el acceso tiene sentido son capturados por la operación de vinculación definida para la mónada; para la mónada Maybe, un valor se vincula solo si existe, y para la mónada IO, un valor se vincula solo después de que se hayan realizado las operaciones anteriores en la secuencia.

Se puede crear una mónada definiendo un constructor de tipo M y dos operaciones:

(En la sección posterior, § Derivación a partir de functores, se puede encontrar una construcción alternativa pero equivalente que utiliza la join función en lugar del operador ).bind

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 tipo simple de entrada y el operador de vinculación maneja el valor monádico devuelto, que se introduce en el siguiente paso de la secuencia.

Normalmente, el operador de vinculación >>=puede contener código exclusivo de la mónada que realiza pasos de cálculo adicionales que no están disponibles en la función recibida como parámetro. Entre cada par de llamadas a funciones compuestas, el operador de vinculación puede inyectar en el valor monádico m ainformación adicional a la que no se puede acceder dentro de la función fy pasarla a lo largo del proceso. 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: tal vez

Un ejemplo de mónada es el Maybetipo. Los resultados nulos indefinidos son un problema particular que muchos lenguajes de procedimiento no proporcionan herramientas específicas para tratar, lo que requiere el uso del patrón de objeto nulo o comprobaciones para probar valores no válidos en cada operación para manejar valores indefinidos. Esto provoca errores y dificulta la creación de software sólido que maneje los errores con elegancia. El Maybetipo obliga al programador a lidiar con estos resultados potencialmente indefinidos definiendo explícitamente los dos estados de un resultado: Just ⌑result⌑o Nothing. Por ejemplo, el programador podría estar construyendo un analizador, que debe devolver un resultado intermedio o señalar una condición que el analizador ha detectado y que el programador también debe manejar. Con solo un poco de sabor funcional adicional encima, este Maybetipo se transforma en una mónada con todas las funciones. [b] : 12,3 páginas 148–151 

En la mayoría de los idiomas, la mónada Maybe también se conoce como tipo de opción , que es simplemente un tipo que marca si contiene o no un valor. Normalmente se expresan como algún tipo de tipo enumerado . En este ejemplo de Rust lo llamaremos Maybe<T>y las variantes de este tipo pueden ser un valor de tipo genérico T o la variante vacía Nothing:.

// <T> representa una enumeración de tipo genérico "T"  Quizás < T > { Sólo ( T ), Nada , }   

Maybe<T>también puede entenderse como un tipo "envolvente", y aquí es donde entra en juego su conexión con las mónadas. En lenguajes con alguna forma de este Maybetipo, hay funciones que ayudan en su uso, como componer funciones monádicas entre sí y probar si a Maybecontiene un valor.

En el siguiente ejemplo codificado, Maybese utiliza un tipo como resultado de funciones que pueden fallar; en este caso, el tipo no devuelve nada si hay una división por cero .

fn  dividir ( x : decimal , y : decimal ) -> Quizás < decimal > { if y == 0 { return Nothing } else { return Just ( x / y ) } } // dividir(1.0, 4.0) -> devuelve Just (0.25) // divide(3.0, 0.0) -> devuelve Nada                  

Una de esas formas de comprobar si a Maybecontiene o no un valor es utilizar ifdeclaraciones.

sea ​​m_x = dividir ( 3.14 , 0.0 ); // ver función de división arriba // La declaración if extrae x de m_x si m_x es la variante Just de Maybe if let Just ( x ) = m_x { println! ( "respuesta: " , x ) } else { println! ( "la división falló, error de división por cero..." ) }               

Otros idiomas pueden tener coincidencia de patrones

dejar resultado = dividir ( 3.0 , 2.0 ); resultado del partido { Solo ( x ) => println! ( "Respuesta: " , x ), Nada => println! ( "la división falló; los atraparemos la próxima vez." ), }             

Las mónadas pueden componer funciones que devuelven Maybe, juntándolas. Un ejemplo concreto podría hacer que una función tome varios Maybeparámetros y devuelva uno Maybecuyo valor sea Nothingcuando cualquiera de los parámetros sea Nothing, como en el siguiente:

fn  chainable_division ( tal vez_x : tal vez <decimal> , tal vez_y: tal vez <decimal> ) - > tal vez <decimal> { match ( tal vez_x , tal vez_y ) { ( Solo ( x ) , Solo ( y )) => { // Si ambas entradas son Just, verifique la división por cero y divida en consecuencia si y == 0 { return Nothing } else { return Just ( x / y ) } }, _ => return Nothing // De lo contrario, return Nothing } } chainable_division ( chainable_division ( Just ( 2.0 ), Sólo ( 0.0 )), Sólo ( 1.0 )); // dentro de chainable_division falla, fuera de chainable_division devuelve Nada                                     

Tener que reescribir funciones para tomar Maybes en este ejemplo concreto requiere mucha repetición (¡mira todas esas Justexpresiones!). En su lugar, podemos usar algo llamado operador de vinculación . (también conocido como "mapa", "mapa plano" o "empujón" [8] : 2205s  ). Esta operación toma una mónada y una función que devuelve una mónada y ejecuta la función en el valor interno de la mónada pasada, devolviendo la mónada de la función.

// Ejemplo de Rust usando ".map". Maybe_x se pasa a través de 2 funciones que devuelven Maybe<Decimal> y Maybe<String> respectivamente. // Al igual que con la composición de funciones normal, las entradas y salidas de las funciones que se alimentan entre sí deben coincidir con los tipos ajustados. (es decir, la función add_one debería devolver Maybe<Decimal> que luego se puede descomprimir en Decimal para la función decimal_to_string) let may_x : Maybe < Decimal > = Just ( 1.0 ) let may_result = may_x . mapa ( add_one ). mapa ( decimal_to_string )      

En Haskell, existe un operador bind , o ( >>=) que permite esta composición monádica en una forma más elegante similar a la composición de funciones . [c] : 150-151 

reducir a la mitad :: Int -> Quizás Int reducir a la mitad x | incluso x = Sólo ( x ` div ` 2 ) | impar x = Nada : este código divide x a la mitad dos veces. se evalúa como Nada si x no es múltiplo de 4 reducir a la mitad x >>= reducir a la mitad                       

Con >>=disponible, chainable_divisionse puede expresar de manera mucho más concisa con la ayuda de funciones anónimas (es decir, lambdas). Observe en la expresión siguiente cómo operan las dos lambdas anidadas en el valor ajustado en la Maybemónada pasada utilizando el operador de vinculación. [d] : 93 

 chainable_division ( mx , mi ) = mx >>= ( λx -> mi >>= ( λy -> Sólo ( x / y )) )               

Lo que se ha mostrado hasta ahora es básicamente una mónada, pero para ser más conciso, la siguiente es una lista estricta de cualidades necesarias para una mónada tal como se define en la siguiente sección.

Tipo monádico
Un tipo ( Maybe) [b] : 148–151 
Unidad de operación
Un convertidor de tipo ( Just(x)) [d] : 93 
operación de enlace
Un combinador para funciones monádicas ( >>=o .flatMap()) [c] : 150–151 

Estas son las 3 cosas necesarias para formar una mónada. Otras mónadas pueden incorporar diferentes procesos lógicos y algunas pueden tener propiedades adicionales, pero todas tendrán estos tres componentes similares. [1] [9]

Definición

La definición más común de mónada en programación funcional, utilizada en el ejemplo anterior, en realidad se basa en una tripleta de Kleisli ⟨T, η, μ⟩ 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. Dado cualquier tipo básico bien definido T , U , una mónada consta de tres partes:

Sin embargo, para calificar plenamente como mónada, estas tres partes también deben respetar algunas leyes:

Algebraicamente, esto significa que cualquier mónada da lugar a una categoría (llamada categoría de Kleisli ) y a un monoide en la categoría de functores (desde valores hasta cálculos), con composición monádica como operador binario en el monoide [8] : 2450s  y unidad como identidad en la mónada.

Uso

El valor del patrón de mónada 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 aporta muchos de los beneficios de la programación puramente funcional . Al cosificar un tipo específico de cálculo, una mónada no sólo 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 también efectos calculados , una expresión monádica se puede reemplazar con su valor en posiciones referencialmente transparentes , de manera muy similar a como pueden ser las expresiones puras, lo que permite muchas técnicas y optimizaciones basadas en la reescritura . [4]

Normalmente, los programadores utilizarán bind para encadenar funciones monádicas en una secuencia, lo que ha llevado a algunos a describir las mónadas como "punto y coma programables", una referencia a cuántos lenguajes imperativos usan punto y coma para separar declaraciones . [1] [5] Sin embargo, se debe enfatizar que las mónadas en realidad no ordenan cálculos; Incluso en lenguajes que las utilizan como características centrales, una 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 mediante la abstracción. [4] [11]

La estructura de la mónada también puede verse como una variación exclusivamente matemática y de tiempo de compilación en el patrón 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 lógica de dominio mientras descargan código repetitivo en módulos predesarrollados, las mónadas pueden incluso considerarse una herramienta para la programación orientada a aspectos . [12]

Otro uso notable de las mónadas es el aislamiento de efectos secundarios, como entrada/salida o estado mutable , en código que de otro modo sería puramente funcional. Incluso los lenguajes puramente funcionales 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 agruparlo 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 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 con tipos subyacentes es poderosa, pero las características únicas y el comportamiento estricto de las mónadas las distinguen de otros conceptos. [13]

Aplicaciones

Las discusiones sobre mónadas específicas normalmente se centrarán 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 utilizando mónadas apropiadas dentro de su lógica central.

Estas son sólo algunas aplicaciones que tienen mónadas en el centro de sus diseños:

Historia

El término "mónada" en 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 idiomas, "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", etc.). [19]

El matemático Roger Godement fue el primero en formular el concepto de mónada (llamándolo "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 necesaria ] Sin embargo, la forma definida anteriormente usando bind fue descrita originalmente en 1965 por el matemático Heinrich Kleisli para demostrar que cualquier mónada podría caracterizarse como una conjunción entre dos functores (covariantes). [20]

A partir de la década de 1980, comenzó a surgir en la comunidad informática una noción vaga del patrón de mónada. Según el investigador de lenguajes de programación Philip Wadler , el científico informático John C. Reynolds anticipó varias facetas del mismo en los años 1970 y principios de los 1980, cuando discutió el valor del estilo de continuación y paso , de la teoría de categorías como una rica fuente de semántica formal y de la distinción de tipos entre valores y cálculos. [4] El lenguaje de investigación Opal , que fue diseñado activamente hasta 1990, también basó efectivamente la E/S en un tipo monádico, pero la conexión no se realizó en ese momento. [21]

El informático Eugenio Moggi fue el primero en vincular explícitamente la mónada de la teoría de categorías con la programación funcional, en un artículo de una conferencia en 1989, [22] seguido de una presentación más refinada en una revista 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 sólo 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 , quienes participaron 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. [23] La comunidad de Haskell continuaría aplicando mónadas a muchos problemas en programación funcional, y en la década de 2010, los investigadores que trabajaban con Haskell finalmente reconocieron que las mónadas son functores aplicativos ; [24] [i] y que tanto las mónadas como las flechas son monoides . [26]

Al principio, la programación con mónadas se limitaba en gran medida a Haskell y sus derivados, pero a medida que la programación funcional ha influido en otros paradigmas, muchos lenguajes han incorporado un patrón de mónadas (en espíritu, aunque no en nombre). Ahora existen formulaciones en Scheme , Perl , Python , Racket , Clojure , Scala , F# y también se han considerado para un nuevo estándar de aprendizaje automático . [ cita necesaria ]

Análisis

Un beneficio del patrón de mónada es que aporta precisión matemática a la composición de los cálculos. 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 funtores) mediante la subtipificación .

Verificando las leyes de las mónadas

Volviendo al Maybeejemplo, se declaró que sus componentes formaban una mónada, pero no se proporcionó ninguna prueba de que satisficiera las leyes de la mónada.

Esto se puede rectificar conectando los detalles de Maybeen un lado de las leyes generales y 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) ⇔ Sólo un otra cosa  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  otro Nada nada terminar si  terminar sisi ma es (Solo a) y f(a) es (Solo b) entonces  (g ∘ f) un de lo contrario, si ma es (Solo a) y f(a) es Nada, entonces Nada demás Nada terminara si

Derivación de functores

Aunque es más raro en informática, se puede utilizar directamente la teoría de categorías, que define una mónada como un functor con dos transformaciones naturales adicionales . [j] Entonces, para comenzar, una estructura requiere una función de orden superior (o "funcional") llamada map para calificar como un functor:

map φ : (a → b) → (ma → mb)

Sin embargo, esto no siempre es un problema importante, especialmente cuando una mónada se deriva de un functor preexistente, tras lo cual la mónada hereda el mapa automáticamente. (Por razones históricas, esto mapse llama fmapen 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 teniendo la misma función. Lo que difiere en esta construcción es que la unidad de ley debe satisfacer; Como el enlace no está definido, la restricción se da en términos de mapa :

(unit ∘ φ) x ↔ ((map φ) ∘ unit) x ↔ x[27]

El salto final del funtor aplicativo a la mónada viene con la segunda transformación, la función de unión (en 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:

(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 una tripleta de Kleisli, la estructura subyacente será la misma y las formas se pueden derivar unas de otras fácilmente:

(map φ) ma ↔ ma >>= (unit ∘ φ)
join(mma) ↔ mma >>= id
ma >>= f ↔ (join ∘ (map f)) ma[28]

Otro ejemplo: lista

La mónada List demuestra naturalmente cómo derivar una mónada a partir de un funtor más simple puede resultar útil. En muchos idiomas, una estructura de lista viene predefinida junto con algunas características básicas, por lo que se supone que un Listconstructor de tipos y un operador de adición (representados con notación infija) ya se proporcionan 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 lista por comprensión 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, lo que requerirá más que una comprensión básica.

Sin embargo, un procedimiento para aplicar cualquier función simple a toda la lista, en otras palabras map , es sencillo:

(mapa φ) listax = [ φ(x1), φ(x2), ..., φ(xn) ]

Ahora, estos dos procedimientos ya ascienden Lista 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 simplemente significa desenvolver una lista externa para agregar las internas que contienen valores:

unirse(listaxlista) = unirse([listax1, listax2, ..., listaxn]) = listax1 ++ listax2 ++ ... ++ listaxn

La mónada resultante no es sólo una lista, sino que cambia de tamaño y se condensa automáticamente a medida que se aplican funciones. bind ahora también se puede derivar con solo una fórmula y luego usarse para alimentar Listvalores a través de una canalización de funciones monádicas:

La Listmónada puede simplificar enormemente el uso de funciones multivalor, como raíces complejas. [29]
(listax >>= f) = unirse ∘ (mapa f) listax

Una aplicación de esta lista monádica es la representación de cálculos no deterministas . Listpuede contener resultados para todas las rutas de ejecución en un algoritmo, luego condensarse en cada paso para "olvidar" qué rutas condujeron a qué resultados (una distinción a veces importante de los algoritmos deterministas y exhaustivos). [ cita necesaria ] Otro beneficio es que los controles 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 tubería. [28]

Una segunda situación donde Listbrilla es la composición de funciones multivaluadas . Por ejemplo, la n - ésima raíz compleja de un número debería producir n números complejos distintos, pero si luego se toma otra m -ésima raíz de esos resultados, los valores finales m•n deberían ser idénticos a la salida de la m•n- ésima raíz . Listautomatiza completamente este problema, condensando los resultados de cada paso en una lista plana y matemáticamente correcta. [29]

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.mw-parser-output .vanchor>:target~.vanchor-text{background-color:#b1d2ff}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 perform en OCaml , expresiones de cálculo en F# , [30] y para comprensión en Scala ). Esto es sólo azúcar sintáctico que disfraza una canalización monádica como un bloque de código ; Luego, el compilador traducirá silenciosamente estas expresiones al código funcional subyacente.

Traducir la addfunción de MaybeHaskell puede mostrar esta característica en acción. Una versión no monádica de addHaskell se ve así:

agregar mx mi = caso mx de Nada -> Nada Solo x -> caso mi de Nada -> Nada Solo y -> Solo ( x + y )                         

En Haskell monádico, returnes el nombre estándar para unidad , además las expresiones lambda deben manejarse explícitamente, pero incluso con estos tecnicismos, la Maybemónada ofrece una definición más clara:

agregar mx mi = mx >>= ( \ x -> mi >>= ( \ y -> devolver ( x + y )))               

Sin embargo, con la notación do, esto se puede resumir aún más en una secuencia muy intuitiva:

agregar mx my = hacer x <- mx y <- mi retorno ( x + y )              

Un segundo ejemplo muestra cómo Maybese puede utilizar en un lenguaje completamente diferente: F#. Con expresiones de cálculo, una función de "división segura" que regresa Nonepara un operando indefinido o una división por cero se puede escribir como:

let  readNum  ()  =  let  s  =  Consola . ReadLine ()  deja  éxito , v  =  Int32 . TryParse ( s )  si  ( succ )  entonces  Algunos ( v )  más  Ningunolet  Secure_div  =  tal vez  {  let !  x  =  leerNum ()  dejar !  y  =  readNum ()  si  ( y  =  0 )  entonces  Ninguno  más  regresa  ( x  /  y )  }

En el momento de la compilación, el compilador "desazúcar" internamente esta función en una cadena más densa de llamadas de enlace :

tal vez . Retraso ( diversión  ()  ->  tal vez . Bind ( readNum () ,  diversión  x  ->  tal vez . Bind ( readNum () ,  diversión  y  ->  si  ( y = 0 )  entonces  Ninguno  más  tal vez . Retorno ( x  /  y ))) )

Para un último ejemplo, incluso las propias leyes generales de las mónadas se pueden expresar 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 ; fx } ; 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 puede reemplazarse con expresiones aparentemente monádicas (o incluso CPS no monádicas). El uso de bind para expresar la canalización monádica aún puede ser 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 transferir hábitos de la programación imperativa, debe evitarse de forma predeterminada y solo usarse cuando sea obviamente superior. [31] [1]

Interfaz general

Cada mónada necesita una implementación específica que cumpla con las leyes de las mónadas, pero todas las mónadas comparten otros aspectos, como la relación con otras estructuras o modismos estándar dentro de un idioma. Como resultado, un lenguaje o biblioteca puede proporcionar una Monadinterfaz general con prototipos de funciones , relaciones de subtipos y otros datos generales. Además de proporcionar una ventaja inicial para el desarrollo y garantizar que una nueva mónada herede características de un supertipo (como funtores), comparar el diseño de una mónada con la interfaz agrega otra capa de control de calidad. [ cita necesaria ]

Operadores

El código monádico a menudo se puede simplificar aún más mediante el uso juicioso de operadores. La función de mapa puede ser especialmente útil ya que funciona en algo más que funciones monádicas ad-hoc; Siempre que una función monádica funcione de manera análoga a un operador predefinido, map se puede utilizar para " elevar " instantáneamente el operador más simple a uno monádico. [k] Con esta técnica, la definición de adddel Maybeejemplo podría resumirse en:

agregar(mx,mi) = mapa (+)

El proceso podría llevarse incluso un paso más allá definiendo addno sólo para Maybe, sino para toda la Monadinterfaz. Al hacer esto, cualquier mónada nueva que coincida con la interfaz de la estructura e implemente su propio mapa heredará inmediatamente una versión mejorada addtambién. El único cambio necesario en la función es generalizar la firma tipográfica:

agregar: (Número de mónada, Número de mónada) → Número de mónada [32]

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) >>= g

Con este operador, las leyes de las mónadas se pueden escribir únicamente 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]

A su vez, lo anterior muestra el significado del bloque "do" en Haskell:

hacer _p <-f(x) _q <-g(_p) h(_q) ↔ ( f >=> g >=> h )(x)

Más ejemplos

Mónada de identidad

La mónada más simple es la mónada Identity , que simplemente anota valores y funciones simples para satisfacer las leyes de la mónada:

ID de nuevo tipo T = Tunidad(x) = x(x >>= f) = f(x)

IdentitySin embargo, en realidad tiene usos válidos, como proporcionar un caso base para transformadores de mónadas recursivos . También se puede utilizar para realizar una asignación de variables básica dentro de un bloque de estilo imperativo. [l] [ cita necesaria ]

Colecciones

Cualquier colección con un anexo adecuado ya es un monoide libre, pero resulta que Listno es la única colección que también tiene una unión bien definida y califica como mónada. Uno puede incluso mutar Listen estas otras colecciones monádicas simplemente imponiendo propiedades especiales en append : [m] [n]

Mónada IO (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 efectos explícitamente . Esta idea es central para la mónada IOIO a de Haskell, donde se puede considerar que un objeto de tipo describe una acción que se realizará en el mundo, y opcionalmente proporciona información sobre el mundo de tipo a. Una acción que no proporciona información sobre el mundo tiene el tipo IO ()"proporcionando" el valor ficticio (). Cuando un programador vincula un IOvalor a una función, la función calcula la siguiente acción a realizar en función de la información sobre el mundo proporcionada por la acción anterior (entradas de usuarios, archivos, etc.). [23] Lo más significativo es que, debido a que el valor de la mónada IO solo puede vincularse a una función que calcula otra mónada IO, la función de vinculación impone una disciplina de una secuencia de acciones donde el resultado de una acción solo puede proporcionarse a una función. que calculará la siguiente acción a realizar. Esto significa que las acciones que no necesitan realizarse nunca lo son, y las acciones que sí deben realizarse tienen una secuencia bien definida, resolviendo el problema de que las acciones (IO) no sean referencialmente transparentes .

Por ejemplo, Haskell tiene varias funciones para actuar en el sistema de archivos más amplio , incluida una que verifica si un archivo existe y otra que elimina un archivo. Sus dos firmas tipo son:

DoesFileExist :: FilePath -> IO Bool removeFile :: FilePath -> IO ()          

El primero está interesado en si un archivo determinado realmente existe y, como resultado, genera un valor booleano dentro de la IOmónada. La segunda función, por otro lado, solo se ocupa de actuar sobre el sistema de archivos para que el IOcontenedor que genera esté vacío.

IOSin embargo, no se limita solo a la E/S de archivos; incluso permite la E/S del usuario y, junto con la sintaxis imperativa, puede imitar un típico "¡Hola, mundo!" programa :

main :: IO () main = do putStrLn "¡Hola mundo!" putStrLn "¿Cuál es tu nombre, usuario?" nombre <- getLine putStrLn ( "Encantado de conocerte, " ++ nombre ++ "!" )                  

Desazucarado, esto se traduce en la siguiente canalización monádica ( >>en Haskell es solo una variante de vinculación para cuando solo importan los efectos monádicos y el resultado subyacente se puede descartar):

main :: IO () main = putStrLn "¡Hola mundo!" >> putStrLn "¿Cuál es tu 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 el progreso de un programa. A veces, es posible que un programador desee 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 se limita principalmente a lenguajes funcionales, este ejemplo implementa una Writermónada en JavaScript . Primero, una matriz (con colas anidadas) permite construir el Writertipo como una lista vinculada . El valor de salida subyacente vivirá en la posición 0 de la matriz, y la posición 1 contendrá implícitamente una cadena de notas auxiliares:

escritor constante = valor => [ valor , []];      

Definir unidad también es muy sencillo:

unidad constante = valor => [ valor , []];      

Solo se necesita una unidad para definir funciones simples que generen Writerobjetos 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 bind , pero para Writer, esto equivale simplemente a concatenar la salida de una función a la lista enlazada de la mónada:

const bind = ( escritor , transformación ) => { const [ valor , registro ] = escritor ; const [ resultado , actualizaciones ] = transformar ( valor ); devolver [ resultado , registro . concat ( actualizaciones )]; };                   

Las funciones de muestra ahora se pueden encadenar usando bind , pero definir una versión de composición monádica (llamada pipelogaquí) 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 realizar cálculos paso a paso y registrarlos para auditarlos más tarde:

pipelog ( unidad ( 4 ), al cuadrado , reducido a la mitad ); // Objeto escritor resultante = [8, ['4 se elevó al cuadrado.', '16 se redujo a la mitad.']]  

Mónada ambiental

Una mónada de entorno (también llamada mónada de lector y mónada de función ) permite que un cálculo dependa de valores de un entorno compartido. El constructor de tipos de mónada asigna un tipo T a funciones de tipo ET , donde E es el tipo del entorno compartido. Las funciones de la mónada son:

Las siguientes operaciones monádicas son útiles:

La operación de solicitud 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 y 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 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 mónada se definen de la siguiente manera:

-- "retorno" 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 ) - Reemplazar 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 :: Estado 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 asigna el tipo de valores de retorno T a 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 unión entre el funtor producto y el funtor exponencial, que existe en cualquier categoría cerrada cartesiana por definición.

Mónada de continuación

Una mónada de continuación [o] con tipo de retorno R asigna el tipo T a funciones de tipo . Se utiliza para modelar el estilo de paso de continuación . Las funciones de retorno y vinculación son las siguientes:

La función de llamada con continuación actual se define de la siguiente manera:

Registro de programa

El siguiente código es un pseudocódigo.Supongamos que tenemos dos funciones fooy bar, con tipos

foo : int -> int barra : int -> int        

Es decir, ambas funciones toman un número entero y devuelven otro número entero. Luego podemos aplicar las funciones en sucesión así:

foo ( barra x )  

Donde el resultado es el resultado de fooaplicado al resultado de baraplicado a x.

Pero supongamos que estamos depurando nuestro programa y nos gustaría agregar mensajes de registro a fooy bar. Entonces cambiamos los tipos así:

foo : int -> int * cadena barra : int -> int * cadena            

De modo que ambas funciones devuelven una tupla, con el resultado de la aplicación como número 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, ya que su tipo de entrada intno es compatible con su tipo de salida int * string. Y aunque nuevamente podemos ganar componibilidad modificando los tipos de cada función para que sean 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 dichas funciones.

En su lugar, definamos una función auxiliar para abstraer este texto estándar:

enlazar : int * cadena -> ( int -> int * cadena ) -> int * cadena              

bindtoma un número entero y una tupla de cadena, luego toma una función (como foo) que se asigna de un número entero a una tupla de número entero y cadena. Su salida es una tupla de entero y cadena, que es el resultado de aplicar la función de entrada al número entero dentro de la tupla de entero y cadena de entrada. De esta manera, sólo necesitamos escribir código repetitivo para extraer el número entero de la tupla una vez, en formato bind.

Ahora hemos recuperado algo de componibilidad. Por ejemplo:

enlazar ( enlazar ( x , s ) barra ) foo    

¿Dónde (x,s)hay un número entero y una tupla de cadena? [pag]

Para aclarar aún más los beneficios, definamos un operador infijo como un alias para bind:

( >>= ) : int * cadena -> ( int -> int * cadena ) -> int * cadena              

Entonces eso t >>= fes 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, dónde ""está la cadena vacía. Entonces definamos una nueva función:

retorno : int -> int * cadena      

Que se envuelve xen la tupla descrita anteriormente.

Ahora tenemos un buen canal para registrar mensajes:

(( retorno x ) >>= barra ) >>= foo     

Eso nos permite registrar más fácilmente los efectos de bary fooen x.

int * stringdenota un valor monádico pseudocodificado . [p] bind y returnson análogas a las funciones correspondientes del mismo nombre. De hecho, int * string, bindy returnforman una mónada.

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 Maybemónada puede considerarse aditiva, con Nothingmzero y una variación del operador OR como mplus . ListTambié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 un contenedor monádico sin valor de un tipo subyacente, pero también se considera un "cero" (en lugar de un "uno") ya que actúa como un absorbente para bind , devolviendo mzero siempre que esté vinculado a una función monádica. Esta propiedad es bilateral 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 se califica una vez como monoide sobre funciones monádicas con bind (como lo hacen todas las mónadas) y nuevamente sobre valores monádicos mediante mplus . [33] [q]

Mónadas libres

A veces, el esquema general de una mónada puede resultar útil, pero ningún patrón simple recomienda una mónada u otra. Aquí es donde entra en juego 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 con los marcadores Justy Nothing, la Maybemónada es de hecho una mónada libre. La Listmónada, por otro lado, no es una mónada libre ya que incluye datos adicionales y específicos sobre las listas (como append ) en su definición. Un último ejemplo es una mónada libre abstracta:

datos Libre f a = Puro a | Gratis ( f ( Libre f a ))            unidad :: a -> Libre f a unidad x = Puro x          enlazar :: Functor f => Libre f a -> ( a -> Libre f b ) -> Libre f b enlazar ( Puro x ) f = f x enlazar ( Libre x ) f = Gratis ( fmap ( \ y -> enlazar y f ) x )                                   

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 intencionado de mónadas libres puede parecer poco práctico al principio, pero su naturaleza formal es particularmente adecuada para problemas sintácticos. Se puede utilizar una mónada gratuita para realizar un seguimiento de la sintaxis y el tipo, dejando la semántica para más adelante y, como resultado, ha encontrado uso en analizadores e intérpretes . [34] Otros también los han aplicado a problemas operativos más dinámicos, como proporcionar iterados dentro de un lenguaje. [35]

Comonadas

Además de generar mónadas con propiedades adicionales, para cualquier mónada determinada, también se puede definir una comonada . Conceptualmente, si las mónadas representan cálculos construidos a partir de valores subyacentes, entonces las comonadas 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 incluye dentro de una mónada, permanece en cuarentena allí junto con cualquier efecto secundario (algo bueno en programación puramente funcional). A veces, sin embargo, el problema tiene más que ver con el consumo de datos contextuales, que los comonads pueden modelar explícitamente.

Técnicamente, una comonada es el dual categórico de una mónada, lo que en términos generales significa que tendrá los mismos componentes requeridos, solo que con la dirección de las firmas tipográficas invertidas . A partir de la definición de mónada centrada en enlace , una comonada consta de:

unidad(wa) : PESO → T
(wa =>> f) : (WU, WU → T) → PESO [r]

extender y count también deben satisfacer duales de las leyes de las mónadas:

unidad ∘ ( (wa =>> f) → wb ) ↔ f(wa) → bwa = >> unidad ↔ 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 comonadas también se pueden derivar de functores usando un dual de unión :

duplicado (wa): PESO → W (PESO)

Sin embargo, si bien operaciones como extender se invierten, una comonada no invierte las funciones sobre las que actúa y, en consecuencia, las comonadas siguen siendo functores con map , no cofunctores . La definición alternativa con duplicado , recuento y mapa también debe respetar sus propias leyes de comonada:

((mapa duplicado) ∘ duplicado) wa ↔ (duplicado ∘ duplicado) wa ↔ wwwa((mapa cuenta) ∘ duplicado) wa ↔ (cuenta ∘ duplicado) wa ↔ wa((mapa mapa φ) ∘ duplicado) wa ↔ (duplicado ∘ (mapa φ)) wa ↔ wwb

Y como ocurre con las mónadas, las dos formas se pueden convertir automáticamente:

(mapa φ) wa ↔ wa =>> (φ ∘ cuenta) wxduplicado wa ↔ wa =>> wx
wa = >> f(wx) ↔ ((mapa f) ∘ duplicado) wa

Un ejemplo sencillo es el comonad Producto , que genera valores basados ​​en un valor de entrada y datos del entorno compartido. De hecho, la Productcomonada es simplemente el dual de la Writermónada y efectivamente lo mismo que la Readermónada (ambas se analizan a continuación). Producty Readerdifieren sólo en qué firmas de funciones aceptan y cómo complementan esas funciones envolviendo o desenvolviendo valores.

Un ejemplo menos trivial es el comonad Stream , que se puede utilizar para representar flujos de datos y adjuntar filtros a las señales entrantes con extend . De hecho, aunque no son tan populares como las mónadas, los investigadores han descubierto que las comonadas son particularmente útiles para el procesamiento de flujos y el modelado de la programación de flujos de datos . [36] [37]

Sin embargo, debido a sus definiciones estrictas, no se pueden simplemente mover objetos de un lado a otro entre mónadas y comonadas. Como 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. [38] [39]

Ver también

Alternativas para modelar cálculos:

Conceptos de diseño relacionados:

Generalizaciones de mónadas:

Notas

  1. ^ 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]
  2. ^ ab La motivación específica para Maybe se puede encontrar en (Hutton 2016). [7]
  3. ^ ab Hutton abstrae a bindque, cuando se le da un tipo a que puede fallar, y una asignación ab que puede fallar, produce un resultado b que puede fallar. (Hutton, 2016) [7]
  4. ^ ab (Hutton 2016) señala que Justo podría denotar éxito y Nada podría denotar fracaso. [7]
  5. ^ Semánticamente, M no es trivial y representa un endofunctor sobre la categoría de todos los valores bien tipificados:
  6. ^ Si bien es 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 :
  7. ^ bind , por otro lado, no es una transformación natural en la teoría de categorías, sino más bien una extensión que eleva un mapeo (de valores a cálculos) a un morfismo entre cálculos:
  8. ^ Estrictamente hablando, es posible que bind no sea formalmente asociativo en todos los contextos porque corresponde a una aplicación dentro del cálculo lambda , no en matemáticas. En un cálculo lambda riguroso, evaluar una vinculación puede requerir primero envolver el término correcto (al vincular dos valores monádicos) o la vinculación misma (entre dos funciones monádicas) en una función anónima para seguir aceptando entradas de la izquierda. [10]
  9. ^ A partir de la versión 7.10.1 de GHC, y en el futuro, Haskell comenzó a aplicar la propuesta Applicative Monad (AMP) de 2014 de Haskell, que requiere la inserción de 7 líneas de código en cualquier módulo existente que use mónadas. [25]
  10. ^ Estas transformaciones naturales se suelen denominar morfismos η, μ. Es decir: η, μ denotan unidad y unión respectivamente.
  11. ^ 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í.
  12. ^ En la teoría de categorías, Identitytambién se puede considerar que la mónada emerge de la conjunción de cualquier funtor con su inverso.
  13. ^ La teoría de categorías considera estas mónadas de colección como conjunciones entre el funtor libre y diferentes functores desde la categoría de conjuntos hasta la categoría de monoides .
  14. ^ Aquí la tarea del programador es construir un monoide apropiado, o quizás elegir un monoide de una biblioteca.
  15. ^ Es posible que el lector desee seguir el hilo de McCann [6] y compararlo con los tipos siguientes.
  16. ^ ab En este caso, se bindha pegado en a stringdonde anteriormente solo integerhabía estado an; es decir, el programador ha construido un complemento : una tupla (x,s), indicada int * stringen el pseudocódigo § anterior.
  17. ^ Algebraicamente, la relación entre los dos aspectos monoides (no conmutativos) se asemeja a la de un casi semiring , y algunas mónadas aditivas califican como tales. Sin embargo, no todas las mónadas aditivas cumplen con las leyes distributivas ni siquiera de una casi semiring. [33]
  18. ^ En Haskell, extender en realidad se define con las entradas intercambiadas, pero como el curry no se utiliza en este artículo, se define aquí como el dual exacto de bind .

Referencias

  1. ^ abcdefg 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.
  2. ^ ab Wadler, Philip (junio de 1990). Comprender las mónadas . Conferencia ACM sobre LISP y programación funcional. Linda, Francia. CiteSeerX 10.1.1.33.5381 . 
  3. ^ abc 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. 
  4. ^ abcde 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 . 
  5. ^ ab Hudak, Paul ; Peterson, Juan; Fasel, José (1999). "Acerca de las mónadas". Una suave introducción a Haskell 98. capítulo 9.
  6. ^ ab Respuesta de CA McCann (23 de julio de 2010 a las 23:39) ¿Cómo y por qué funciona la mónada Haskell Cont?
  7. ^ abc Graham Hutton (2016) Programación en Haskell, segunda edición
  8. ^ ab Beckerman, Brian (21 de noviembre de 2012). "No temas a la Mónada". YouTube .
  9. ^ Spivey, Mike (1990). "Una teoría funcional de las excepciones" (PDF) . Ciencia de la programación informática . 14 (1): 25–42. doi : 10.1016/0167-6423(90)90056-J .
  10. ^ "Leyes de mónadas". HaskellWiki . haskell.org . Consultado el 14 de octubre de 2018 .
  11. ^ "Lo que no es una mónada". 7 de octubre de 2018.
  12. ^ De Meuter, Wolfgang (1997). Mónadas como fundamento teórico de AOP (PDF) . Taller Internacional sobre Programación Orientada a Aspectos en ECOOP. Jyväskylä, Finlandia. CiteSeerX 10.1.1.25.8262 . 
  13. ^ "Mónada (sin metáforas)". HaskellWiki . 1 de noviembre de 2009 . Consultado el 24 de octubre de 2018 .
  14. ^ 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.
  15. ^ Stewart, Don (17 de mayo de 2007). "Enrolle su propio administrador de ventanas: 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 .
  16. ^ Benton, Nick (2015). "Mónadas categóricas y programación informática" (PDF) . Impacto de la Sociedad Matemática de Londres 150 historias . 1 . Consultado el 19 de noviembre de 2018 .
  17. ^ Kiselyov, Olag (2007). "Continuaciones delimitadas en sistemas operativos". Modelado y uso del contexto . Apuntes de conferencias sobre informática. vol. 4635. Springer Berlín Heidelberg. páginas 291--302. doi :10.1007/978-3-540-74255-5_22. ISBN 978-3-540-74255-5.
  18. ^ Meijer, Erik (27 de marzo de 2012). "Su mouse es una base de datos". Cola ACM . 10 (3): 20–33. doi : 10.1145/2168796.2169076 .
  19. ^ Iverson, Kenneth (septiembre de 1987). "Un diccionario de APL". Cuádruple de cotizaciones de APL . 18 (1): 5–40. doi :10.1145/36983.36984. ISSN  1088-6826. S2CID  18301178 . Consultado el 19 de noviembre de 2018 .
  20. ^ Kleisli, Heinrich (1965). "Cada construcción estándar está inducida por un par de functores adjuntos" (PDF) . Actas de la Sociedad Matemática Estadounidense . 16 (3): 544–546. doi : 10.1090/S0002-9939-1965-0177024-4 . Consultado el 19 de noviembre de 2018 .
  21. ^ Peter Pepper, ed. (noviembre de 1997). The Programming Language Opal (Informe técnico) (5ª edición corregida). Fachbereich Informatik, Universidad Técnica de Berlín. CiteSeerX 10.1.1.40.2748 . 
  22. ^ Moggi, Eugenio (junio de 1989). Cálculo lambda computacional y mónadas (PDF) . Cuarto Simposio Anual de Lógica en Informática. Arboleda del Pacífico, California. CiteSeerX 10.1.1.26.2787 . 
  23. ^ ab 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 . 
  24. ^ Clase de tipos Brent Yorgey
  25. ^ Desbordamiento de pila (8 de septiembre de 2017) La definición de una nueva mónada en haskell no genera ninguna instancia de aplicativo
  26. ^ Monoides de Brent Yorgey
  27. ^ "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 .
  28. ^ ab 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 .
  29. ^ ab Piponi, Dan (7 de agosto de 2006). "¡Podrías haber inventado las mónadas! (Y tal vez 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 .
  30. ^ "Algunos detalles sobre las expresiones informáticas de F#". 21 de septiembre de 2007 . Consultado el 9 de octubre de 2018 .
  31. ^ "Hacer notación considerada dañina". HaskellWiki . Consultado el 12 de octubre de 2018 .
  32. ^ 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 .
  33. ^ ab Rivas, Exequiel; Jaskelioff, Mauro; Schrijvers, Tom (julio de 2015). De monoides a casi semirrenos: 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 . 
  34. ^ Swierstra, Wouter (2008). «Tipos de datos a la carta» (PDF) . Perla funcional. Revista de programación funcional . 18 (4). Prensa de la Universidad de Cambridge: 423–436. CiteSeerX 10.1.1.101.4131 . doi :10.1017/s0956796808006758. ISSN  1469-7653. S2CID  21038598. 
  35. ^ Kiselyov, Oleg (mayo de 2012). Schrijvers, Tom; Thiemann, Peter (eds.). Iterados (PDF) . Simposio Internacional sobre Programación Lógica y Funcional. Apuntes de conferencias sobre informática. vol. 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.
  36. ^ 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 Centroeuropea. Apuntes de conferencias sobre informática. vol. 4164. Budapest, Hungría: Springer-Verlag. págs. 135-167. CiteSeerX 10.1.1.62.2047 . ISBN  978-3-540-46845-5.
  37. ^ Uustalu, Tarmo; Vene, Varmo (junio de 2008). "Nociones comonádicas de computación". Apuntes Electrónicos en Informática Teórica . 203 (5). Elsevier: 263–284. doi :10.1016/j.entcs.2008.05.029. ISSN  1571-0661.
  38. ^ Poder, Juan; Watanabe, Hiroshi (mayo de 2002). «Combinando una mónada y una comonada» (PDF) . Informática Teórica . 280 (1–2). Elsevier: 137-162. CiteSeerX 10.1.1.35.4130 . doi :10.1016/s0304-3975(01)00024-x. ISSN  0304-3975. 
  39. ^ Gaboardi, Marco; Katsumata, Shin-ya; Huerto, Domingo; Breuvart, Flavien; Uustalu, Tarmo (septiembre de 2016). Combinación de efectos y coefectos mediante gradación (PDF) . XXI 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:

Tutoriales:

Casos interesantes: