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 computación adicional. Además de definir un tipo monádico envolvente , las mónadas definen dos operadores : uno para envolver un valor en el tipo de mónada y otro para componer funciones que generen valores del tipo de mónada (se conocen como funciones monádicas ). Los lenguajes de propósito general usan 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 usan mónadas para convertir secuencias complicadas de funciones en conductos sucintos 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] La investigación que comenzó a fines de la década de 1980 y principios de la de 1990 estableció 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 cualquier mónada debe satisfacer y que 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 se pueden usar para implementar características convenientes del lenguaje. Algunos lenguajes, como Haskell , incluso ofrecen definiciones predefinidas en sus bibliotecas principales para la estructura general de la mónada y las instancias comunes. [1] [5]

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, una mónada se puede utilizar cuando el acceso sin restricciones a un valor no es apropiado por razones específicas del escenario. En el caso de la mónada Maybe, es porque el valor puede no existir. En el caso de la mónada IO, es porque el valor puede no ser conocido todavía, como cuando la mónada representa la 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 se capturan mediante la operación de enlace definida para la mónada; para la mónada Maybe, un valor se enlaza solo si existe, y para la mónada IO, un valor se enlaza 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:

(Una construcción alternativa pero equivalente que utiliza la join función en lugar del bindoperador se puede encontrar en la sección posterior § Derivación de funtores ).

Con estos elementos, el programador compone una secuencia de llamadas de función (una "tubería") con varios operadores de enlace encadenados en una expresión. Cada llamada de función transforma su valor de tipo simple de entrada y el operador de enlace maneja el valor monádico devuelto, que se introduce en el siguiente paso de la secuencia.

Normalmente, el operador de enlace >>=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 enlace puede inyectar en el valor monádico m ainformación adicional que no es accesible dentro de la función fy pasarla a lo largo del proceso de ejecución. También puede ejercer un control más preciso del flujo de ejecución, por ejemplo, llamando a la función solo bajo ciertas 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 para el que muchos lenguajes procedimentales no proporcionan herramientas específicas, lo que requiere el uso del patrón de objeto nulo o comprobaciones para comprobar si hay valores no válidos en cada operación para manejar valores indefinidos. Esto provoca errores y dificulta la creación de software robusto que gestione los errores de forma elegante. 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 bien señalar una condición que el analizador ha detectado y que el programador también debe manejar. Con solo un poco de condimento funcional adicional, 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 lenguajes, la mónada Maybe también se conoce como un 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.

// La <T> representa una enumeración  genérica de tipo "T" Maybe < T > { Just ( T ), Nothing , }   

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 los lenguajes con alguna forma del 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  divide ( x : Decimal , y : Decimal ) -> Maybe < Decimal > { if y == 0 { return Nothing } else { return Just ( x / y ) } } // divide(1.0, 4.0) -> devuelve Just(0.25) // divide(3.0, 0.0) -> devuelve Nothing                  

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

sea ​​m_x = divide ( 3.14 , 0.0 ); // vea la función de división anterior // La declaración if extrae x de m_x if 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

deje que resultado = dividir ( 3.0 , 2.0 ); coincidir resultado { Solo ( x ) => println! ( "Respuesta: " , x ), Nada => println! ( "la división falló; los conseguiremos la próxima vez." ), }             

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

fn  chainable_division ( maybe_x : Maybe < Decimal > , maybe_y : Maybe < Decimal > ) -> Maybe < Decimal > { match ( maybe_x , maybe_y ) { ( Just ( x ), Just ( y )) => { // Si ambas entradas son Just, comprueba la división por cero y divide en consecuencia if y == 0 { return Nothing } else { return Just ( x / y ) } }, _ => return Nothing // De lo contrario, devuelve Nothing } } chainable_division ( chainable_division ( Just ( 2.0 ), Just ( 0.0 )), Just ( 1.0 )); // dentro de chainable_division falla, fuera de chainable_division devuelve Nothing                                     

En lugar de repetir Justexpresiones, podemos usar algo llamado operador de enlace (también conocido como "map", "flatmap" o "shove" [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 desde la función.

// Ejemplo de Rust que utiliza ".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 encapsulados. (es decir, la función add_one debe devolver un Maybe<Decimal> que luego se puede desempaquetar en un Decimal para la función decimal_to_string) let maybe_x : Maybe < Decimal > = Just ( 1.0 ) let maybe_result = maybe_x . map ( add_one ). map ( decimal_to_string )      

En Haskell, hay 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 

halve :: Int -> Maybe Int halve x | even x = Just ( x ` div ` 2 ) | odd x = Nothing -- Este código divide x por la mitad dos veces. Se evalúa como Nothing si x no es un múltiplo de 4 halve x >>= halve                       

Si >>=está disponible, chainable_divisionse puede expresar de forma mucho más sucinta con la ayuda de funciones anónimas (es decir, lambdas). Observe en la expresión siguiente cómo las dos lambdas anidadas operan cada una sobre el valor encapsulado en la Maybemónada pasada utilizando el operador de enlace. [d] : 93 

 división_encadenable ( mx , my ) = mx >>= ( λx -> my >>= ( λy -> Just ( x / y )) )               

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

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

Estas son las tres cosas necesarias para formar una mónada. Otras mónadas pueden incorporar procesos lógicos diferentes y algunas pueden tener propiedades adicionales, pero todas ellas 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, se basa en realidad 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 dos definiciones dará como resultado una mónada válida. Dados los tipos básicos T y U bien definidos , una mónada consta de tres partes:

Sin embargo, para poder ser consideradas 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 funtores (desde valores hasta cálculos), con composición monádica como operador binario en el monoide [8] : 2450s  y unidad como identidad en el monoide.

Uso

El valor del patrón mónada va más allá de simplemente condensar el código y proporcionar un vínculo al razonamiento matemático. Cualquiera sea el lenguaje o paradigma de programación predeterminado que utilice un desarrollador, seguir el patrón mónada aporta muchos de los beneficios de la programación puramente funcional . Al materializar 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 manera 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 puede reemplazarse con su valor en posiciones referencialmente transparentes , de manera muy similar a las expresiones puras, lo que permite muchas técnicas y optimizaciones basadas en la reescritura . [4]

Por lo general, los programadores usan 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 cómo muchos lenguajes imperativos usan punto y coma para separar declaraciones . [1] [5] Sin embargo, las mónadas en realidad no ordenan los cálculos; incluso en lenguajes que las usan 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] [11]

La estructura de mónada también puede verse como una variación exclusivamente matemática y de tiempo de compilación del patrón decorador . Algunas mónadas pueden pasar datos adicionales que son inaccesibles para las funciones, y algunas incluso ejercen un control más preciso sobre la ejecución, por ejemplo, solo invocando una función bajo ciertas condiciones. Debido a que permiten a los programadores de aplicaciones implementar la lógica del dominio mientras descargan código repetitivo en módulos desarrollados previamente, las mónadas incluso pueden 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 agrupándolo en una mónada distinta. [4]

Si un lenguaje no admite mónadas por defecto, 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 de permanecer agnóstico sobre los detalles operativos mientras trabaja en tipos subyacentes es poderosa, pero las características únicas y el comportamiento estricto de las mónadas las distinguen de otros conceptos. [13]

Aplicaciones

Los debates sobre mónadas específicas se centran normalmente en la solución de un problema de implementación específico, 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 adecuadas dentro de su lógica central.

Estas son solo algunas aplicaciones que tienen mónadas en el corazón de sus diseños:

Historia

El término "mónada" en programación data de los lenguajes de programación APL y J , que tienden a ser puramente funcionales. Sin embargo, en esos lenguajes, "mónada" es solo una forma abreviada de referirse a una función que toma un parámetro (una función con dos parámetros es una "díada", y así sucesivamente). [19]

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

A partir de la década de 1980, una noción vaga del patrón mónada comenzó a surgir en la comunidad de la ciencia informática. Según el investigador de lenguajes de programación Philip Wadler , el científico informático John C. Reynolds anticipó varias facetas de este patrón en la década de 1970 y principios de la de 1980, cuando analizó el valor del estilo de paso de continuación , 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 se diseñó 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 científico 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 conferencia en 1989, [22] seguido por una presentación más refinada en una revista en 1991. En trabajos anteriores, varios científicos informáticos habían avanzado en el uso de la 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 , quienes estuvieron involucrados en la especificación de Haskell. En particular, Haskell utilizó un modelo problemático de "flujo diferido" hasta la v1.2 para reconciliar la E/S con la evaluación diferida , hasta que cambió a una interfaz monádica más flexible. [23] La comunidad 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 funtores 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 influyó en otros paradigmas, muchos lenguajes incorporaron un patrón de mónada (en espíritu, si 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 ML . [ cita requerida ]

Análisis

Una ventaja 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 utilizar las leyes de mónada para comprobar la validez de una instancia, sino que también se pueden utilizar características de estructuras relacionadas (como los funtores) mediante la subtipificación .

Verificación de las leyes de las mónadas

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

Esto se puede corregir introduciendo 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) ⇔ Solo una De lo contrario  o Nada ⇔ Nada terminar 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 De otra manera, de  otra manera Nada, nada fin si  fin sisi ma es (Solo a) y f(a) es (Solo b) entonces  (g ∘ f) a De lo contrario, si ma es (solo a) y f(a) no es nada, entonces Nada demás Nada terminar si

Derivación a partir de funtores

Aunque es menos frecuente en informática, se puede utilizar directamente la teoría de categorías, que define una mónada como un funtor con dos transformaciones naturales adicionales . [j] Entonces, para comenzar, una estructura requiere una función de orden superior (o "funcional") llamada mapa 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, en cuyo caso la mónada hereda map automáticamente. (Por razones históricas, en Haskell mapse le llama así fmap).

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 unidad caracteriza a 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 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 no se define el vínculo , 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 llega con la segunda transformación, la función de unión (en teoría de categorías esta es una transformación natural usualmente 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 la mónada:

(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[28]

Otro ejemplo: Lista

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 se supone que ya se han proporcionado aquí un Listconstructor de tipos y un operador de anexión (representado con ++la notación infija for).

Incrustar un valor simple en una lista también es trivial en la mayoría de los lenguajes:

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 bind 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 sobre toda la lista, en otras palabras map , es sencillo:

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

Ahora bien, estos dos procedimientos ya promueven Listun funtor aplicativo. Para calificar plenamente como 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:

unir(xlistlist) = unir([xlist1, xlist2, ..., xlistn]) = listax1 ++ listax2 ++ ... ++ listaxn

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

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

Una aplicación para esta lista monádica es representar el cálculo no determinista . ListPuede 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 las verificaciones 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 en la que Listbrilla es la composición de funciones multivaluadas . Por ejemplo, la raíz compleja n -ésima de un número debería producir n números complejos distintos, pero si luego se toma otra raíz m -ésima de esos resultados, los valores m•n finales deberían ser idénticos a la salida de la raíz m•n -ésima. automatiza completamente este problema, condensando los resultados de cada paso en una lista plana y matemáticamente correcta. [29]List

Técnicas

Las mónadas ofrecen oportunidades para técnicas interesantes que van 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áctico.mw-parser-output .vanchor>:target~.vanchor-text{background-color:#b1d2ff}@media screen{html.skin-theme-clientpref-night .mw-parser-output .vanchor>:target~.vanchor-text{background-color:#0f4dc9}}@media screen and (prefers-color-scheme:dark){html.skin-theme-clientpref-os .mw-parser-output .vanchor>:target~.vanchor-text{background-color:#0f4dc9}}notación do

Aunque usar bind abiertamente suele tener sentido, muchos programadores prefieren una sintaxis que imite las declaraciones imperativas (llamada do-notation en Haskell, perform-notation en OCaml , expresiones computacionales en F# , [30] 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 ; el compilador luego traducirá silenciosamente estas expresiones en código funcional subyacente.

La traducción de la addfunción de a MaybeHaskell puede mostrar esta característica en acción. Una versión no monádica de adden Haskell se ve así:

agregar mx my = caso mx de Nada -> Nada Solo x -> caso my 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 permite una definición más clara:

añadir mx mi = mx >>= ( \ x -> mi >>= ( \ y -> return ( x + y )))               

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

suma mx my = hacer x <- mx y <- my return ( 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 retorna Nonepara un operando indefinido o una división por cero se puede escribir como:

deje que  readNum  ()  =  deje que  s =  Console.ReadLine  ( ) deje que succ , v = Int32.TryParse ( s ) si ( succ ) entonces Algún ( v ) de lo contrario Ninguno          deje que  secure_div  =  tal vez  {  deje que !  x  =  readNum ()  deje que !  y  =  readNum ()  si  ( y  =  0 )  entonces  Ninguno  de lo contrario  devuelva  ( x  /  y )  }

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

tal vez . Delay ( fun  ()  ->  tal vez . Bind ( readNum () ,  fun  x  ->  tal vez . Bind ( readNum () ,  fun  y  ->  if  ( y = 0 )  then  None  else  tal vez . Return ( x  /  y ))))

Como último ejemplo, incluso las leyes generales de la mónada pueden expresarse en notación do:

hacer { x <- devolver v ; f x } == hacer { f v } hacer { x <- m ; devolver x } == hacer { m } hacer { y <- hacer { x < - m ; f x }; g y } == hacer { x <- m ; y <- f x ; g y }                                                     

Interfaz general

Cada mónada necesita una implementación específica que cumpla con las leyes de mónadas, pero otros aspectos como la relación con otras estructuras o modismos estándar dentro de un lenguaje son compartidos por todas las mónadas. Como resultado, un lenguaje o biblioteca puede proporcionar una Monadinterfaz general con prototipos de funciones , relaciones de subtipos 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 los funtores), verificar el diseño de una mónada con respecto a la interfaz agrega otra capa de control de calidad. [ cita requerida ]

Operadores

El código monádico puede simplificarse aún más mediante el uso juicioso de operadores. La función map 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, map puede usarse para " elevar " instantáneamente el operador más simple a uno monádico. [k] Con esta técnica, la definición del addejemplo Maybepodría resumirse en:

agregar(mx,my) = mapa (+)

El proceso podría llevarse incluso un paso más allá definiendo addno solo for Maybe, sino toda la Monadinterfaz. Al hacer esto, cualquier nueva mónada que coincida con la estructura interface e implemente su propio mapa heredará inmediatamente una versión elevada de addtoo. 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 [31]

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 la mónada se pueden escribir solo en términos de funciones, resaltando 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 Identidad , que simplemente anota valores y funciones simples para satisfacer las leyes de la mónada:

nuevo tipo Id 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 asignaciones de variables básicas dentro de un bloque de estilo imperativo. [l] [ cita requerida ]

Colecciones

Cualquier colección con un append 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 una 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 explícitamente los efectos. Esta idea es central para la mónada IO de Haskell , donde un objeto de tipo IO apuede verse como una descripción de una acción a realizar en el mundo, proporcionando opcionalmente 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 (entrada de usuarios, archivos, etc.). [23] Lo más significativo es que, debido a que el valor de la mónada IO solo se puede vincular 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 se puede proporcionar a una función que calculará la siguiente acción a realizar. Esto significa que las acciones que no necesitan ser realizadas nunca se realizan, y las acciones que sí necesitan ser realizadas tienen una secuencia bien definida, lo que resuelve el problema de que las acciones (IO) no sean referencialmente transparentes .

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

doesFileExist :: RutaArchivo -> IO Bool removeFile :: RutaArchivo -> IO ()          

La primera se interesa por si un archivo determinado existe realmente 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, por lo que el IOcontenedor que genera está vacío.

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

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

Sin azúcar, esto se traduce en la siguiente canalización monádica ( >>en Haskell es solo una variante de bind 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 >>= ( \ name -> putStrLn ( "Encantado de conocerte, " ++ name ++ "!" ))                     

Mónada del escritor (JavaScript)

Otra situación habitual es la de mantener un archivo de registro o informar de algún otro modo 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 elaboración de perfiles o depuración . La mónada Writer puede encargarse de estas tareas generando una salida auxiliar que se acumula paso a paso.

Para demostrar que el patrón de mónada no está restringido a lenguajes principalmente funcionales, este ejemplo implementa una Writermónada en JavaScript . Primero, una matriz (con colas anidadas) permite construir el Writertipo como una lista enlazada . El valor de salida subyacente estará en la posición 0 de la matriz, y la posición 1 contendrá implícitamente una cadena de notas auxiliares:

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

Definir unidad también es muy sencillo:

const unidad = 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 elevado al cuadrado.` ]]; const reducido a la mitad = x => [ x / 2 , [ ` ${ x } fue reducido a la mitad.` ]];                

Una verdadera mónada aún 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 , transformar ) => { const [ valor , registro ] = escritor ; const [ resultado , actualizaciones ] = transformar ( valor ); return [ resultado , registro . concat ( actualizaciones )]; };                   

Las funciones de muestra ahora se pueden encadenar entre sí mediante 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 . reduce ( bind , escritor );       

El resultado final es una clara separación de preocupaciones entre la ejecución de los cálculos y su registro para una auditoría posterior:

pipelog ( unidad ( 4 ), al cuadrado , reducido a la mitad ); // Objeto de escritor resultante = [8, ['4 fue elevado al cuadrado.', '16 fue reducido 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 de mónada asigna un tipo T a funciones de tipo ET , donde E es el tipo del entorno compartido. Las funciones de mónada son:

Las siguientes operaciones monádicas son útiles:

La operación de solicitud se utiliza para recuperar el contexto actual, mientras que la operación local ejecuta un cálculo en un subcontexto modificado. Al igual que en una mónada de estado, los cálculos en la mónada de entorno se pueden invocar 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 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 la mónada 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 -> let ( x , s ) = m r in ( 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. modify f = \ s -> ( () , f s ) -- Actualiza el estado                     

Otra operación aplica una mónada de estado a un estado inicial dado:

estadoDeEjecución :: Estado s a -> s -> ( a , s ) estadoDeEjecución 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 de enlace 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 cartesiana cerrada 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 enlace son las siguientes:

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

Registro de programas

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

foo : int -> int bar : int -> int        

Es decir, ambas funciones toman un entero y devuelven otro entero. Luego podemos aplicar las funciones en sucesión de la siguiente manera:

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 queremos agregar mensajes de registro a fooy bar. Por lo tanto, cambiamos los tipos de la siguiente manera:

foo : int -> int * cadena bar : int -> int * cadena            

Para que ambas funciones devuelvan 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.

Lamentablemente, 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 podemos volver a ganar capacidad de composición 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 entero de la tupla, lo que se volvería tedioso a medida que aumenta la cantidad de dichas funciones.

En lugar de eso, definamos una función auxiliar para abstraer este código estándar para nosotros:

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

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

Ahora hemos recuperado cierta componibilidad. Por ejemplo:

enlazar ( enlazar ( x , s ) bar ) foo    

Donde (x,s)es una tupla de números enteros y cadenas. [p]

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              

Así que t >>= fes lo mismo que bind t f.

Entonces el ejemplo anterior se convierte en:

(( x , s ) >>= barra ) >>= foo    

Por último, definimos una nueva función para evitar escribir (x, "")cada vez que deseamos crear un mensaje de registro vacío, donde ""es la cadena vacía.

retorno : int -> int * cadena      

Que se envuelve xen la tupla descrita anteriormente.

El resultado es una canalización para registrar mensajes:

(( devuelve x ) >>= barra ) >>= foo     

Esto 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, bind, y returnforman una mónada.

Mónadas aditivas

Una mónada aditiva es una mónada dotada de un operador binario asociativo cerrado adicional mplus y un elemento 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 absorbedor para bind , devolviendo mzero siempre que se enlaza a una función monádica. Esta propiedad es bilateral y bind también devolverá mzero cuando cualquier valor esté enlazado 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 vínculo (como lo hacen todas las mónadas), y nuevamente sobre valores monádicos a través de mplus . [32] [q]

Mónadas libres

A veces, el esquema general de una mónada puede ser útil, pero no existe ningún patrón simple que recomiende una u otra mónada. 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 ninguna restricción específica más allá de las propias leyes de la mónada. 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 ninguna semántica más profunda.

Por ejemplo, al trabajar completamente a través de 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 incorpora hechos adicionales y específicos sobre listas (como append ) a su definición. Un último ejemplo es una mónada libre abstracta:

datos Libre f a = Puro a | Libre ( f ( Libre f a ))            unidad :: a -> Libre f a unidad x = Pura x          bind :: Functor f => Free f a -> ( a -> Free f b ) -> Free f b bind ( Pure x ) f = f x bind ( Free x ) f = Free ( fmap ( \ y -> bind y f ) x )                                   

Sin embargo, las mónadas libres no están restringidas a una lista enlazada como en este ejemplo, y pueden construirse alrededor de otras estructuras como árboles .

El uso intencional de mónadas libres puede parecer poco práctico al principio, pero su naturaleza formal es particularmente adecuada para problemas sintácticos. Una mónada libre se puede utilizar para rastrear la sintaxis y el tipo, dejando la semántica para más adelante, y como resultado, se ha encontrado uso en analizadores sintácticos e intérpretes . [33] Otros también las han aplicado a problemas operativos más dinámicos, como proporcionar iteradores dentro de un lenguaje. [34]

Comónadas

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 de vuelta a los 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 cualquier efecto secundario (algo bueno en la programación puramente funcional). Sin embargo, a veces, un problema tiene más que ver con el consumo de datos contextuales, que las comónadas pueden modelar explícitamente.

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

cuenta(wa) : WT → T
(wa =>> f) : (WU, WU → T) → WT [r]

extender y contar también deben satisfacer los duales de las leyes de la mónada:

unidad ∘ ( (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 comonadas también pueden derivarse de funtores usando un dual de join :

duplicado(wa): WT → W (WT)

Sin embargo, si bien las operaciones como extend se invierten, una comónada no invierte las funciones sobre las que actúa y, en consecuencia, las comónadas siguen siendo funtores con map , no cofuntores . La definición alternativa con duplicate , counit y map también debe respetar sus propias leyes de comónadas:

((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 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) ∘ duplicado) wa

Un ejemplo simple es la comonada Product , que genera valores basados ​​en un valor de entrada y datos de entorno compartidos. 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 solo en las firmas de función que aceptan y en cómo complementan esas funciones envolviendo o desenvolviendo valores.

Un ejemplo menos trivial es la comónada 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 comónadas son particularmente útiles para el procesamiento de flujos y la programación de modelado de flujos de datos . [35] [36]

Sin embargo, debido a sus definiciones estrictas, no es posible 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 comónado es un área activa de investigación. [37] [38]

Véase también

Alternativas para los cálculos de modelado:

Conceptos de diseño relacionados:

Generalizaciones de las mónadas:

Notas

  1. ^ Debido al hecho de que las funciones en múltiples variables libres son comunes en programación, las mónadas descritas en este artículo son técnicamente lo que los teóricos de categorías llamarían mónadas fuertes . [3]
  2. ^ La motivación específica para Quizás 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 "Sólo" 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 funtores :
  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 una aplicación (de valores a cálculos) a un morfismo entre cálculos:
  8. ^ Estrictamente hablando, el enlace puede no ser formalmente asociativo en todos los contextos porque corresponde a una aplicación dentro del cálculo lambda , no de las matemáticas. En el cálculo lambda riguroso, evaluar un enlace puede requerir primero envolver el término derecho (al enlazar dos valores monádicos) o el enlace mismo (entre dos funciones monádicas) en una función anónima para seguir aceptando la entrada desde la izquierda. [10]
  9. ^ A partir de la versión 7.10.1 de GHC, y en adelante, Haskell comenzó a aplicar la propuesta Applicative Monad (AMP) de Haskell de 2014, 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 denotar como morfismos η, μ. Es decir: η, μ denotan unidad y unión respectivamente.
  11. ^ Algunos lenguajes como Haskell incluso proporcionan un seudónimo para map en otros contextos llamado lift, junto con múltiples versiones para diferentes cantidades de parámetros, un detalle que se ignora aquí.
  12. ^ En la teoría de categorías, la Identitymónada también puede considerarse como algo que surge de la adjunción de cualquier funtor con su inverso.
  13. ^ La teoría de categorías considera a estas mónadas de colección como adjuntos entre el funtor libre y diferentes funtores de la categoría de conjuntos a 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. ^ El lector quizá desee seguir el hilo de McCann [6] y compararlo con los tipos que aparecen a continuación.
  16. ^ ab En este caso, el bindha pegado en a stringdonde anteriormente sólo integerhabía an ; es decir, el programador ha construido un adjunto : una tupla (x,s), denotada int * stringen el pseudocódigo § anterior.
  17. ^ Algebraicamente, la relación entre los dos aspectos monoides (no conmutativos) se asemeja a la de un semirredondo cercano , y algunas mónadas aditivas sí califican como tales. Sin embargo, no todas las mónadas aditivas cumplen las leyes distributivas ni siquiera de un semirredondo cercano. [32]
  18. ^ En Haskell, extend en realidad se define con las entradas intercambiadas, pero como en este artículo no se utiliza currying, se define aquí como el dual exacto de bind .

Referencias

  1. ^ abcdef O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). "Mónadas". Real World Haskell. Sebastopol, California: O'Reilly Media. Capítulo 14. ISBN 978-0596514983.
  2. ^ ab Wadler, Philip (junio de 1990). Comprensión de mónadas . Conferencia ACM sobre LISP y programación funcional. Niza, 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 . 19.° Simposio anual de la ACM sobre principios de lenguajes de programación. Albuquerque, Nuevo México. CiteSeerX 10.1.1.38.9516 . 
  5. ^ ab Hudak, Paul ; Peterson, John; Fasel, Joseph (1999). "Acerca de las mónadas". Una introducción sencilla a Haskell 98. capítulo 9.
  6. ^ ab CA Respuesta de 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 2.ª 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) . Science of Computer Programming . 14 (1): 25–42. doi : 10.1016/0167-6423(90)90056-J .
  10. ^ "Leyes de las 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). Monads as a theory foundation for 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". Real World Haskell. Sebastopol, California: O'Reilly Media. Capítulo 16. ISBN 978-0596514983.
  15. ^ Stewart, Don (17 de mayo de 2007). "Crea tu propio gestor de ventanas: seguimiento del foco 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) . London Mathematical Society Impact150 Stories . 1. Consultado el 19 de noviembre de 2018 .
  17. ^ Kiselyov, Olag (2007). "Continuaciones delimitadas en sistemas operativos". Modelado y uso del contexto . Apuntes de clase en informática. Vol. 4635. Springer Berlin 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 ratón es una base de datos". ACM Queue . 10 (3): 20–33. doi : 10.1145/2168796.2169076 .
  19. ^ Iverson, Kenneth (septiembre de 1987). "Un diccionario de APL". APL Quote Quad . 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). «Toda construcción estándar es inducida por un par de funtores 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 .
  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 sobre lógica en informática. Pacific Grove, 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 la ACM sobre Principios de Lenguajes de Programación. Charleston, Carolina del Sur. CiteSeerX 10.1.1.53.2504 . 
  24. ^ Brent Yorgey Tipografía pedia
  25. ^ Desbordamiento de pila (8 de septiembre de 2017) La definición de una nueva mónada en Haskell no genera ninguna instancia para Applicative
  26. ^ Brent Yorgey Monoides
  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). «Monads as Containers» (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 quizá ya lo hayas hecho)». A Neighborhood of Infinity . Archivado desde el original el 24 de octubre de 2018. Consultado el 16 de octubre de 2018 .
  30. ^ "Algunos detalles sobre las expresiones computacionales de F#". 21 de septiembre de 2007. Consultado el 9 de octubre de 2018 .
  31. ^ Giles, Brett (12 de agosto de 2013). "Lifting". HaskellWiki . Haskell.org. Archivado desde el original el 29 de enero de 2018 . Consultado el 25 de noviembre de 2018 .
  32. ^ ab Rivas, Exequiel; Jaskelioff, Mauro; Schrijvers, Tom (julio de 2015). De monoides a semianillos cercanos: la esencia de MonadPlus y Alternative (PDF) . 17.° Simposio Internacional ACM sobre Principios y Práctica de Programación Declarativa. Siena, Italia. CiteSeerX 10.1.1.703.342 . 
  33. ^ Swierstra, Wouter (2008). "Tipos de datos a la carta" (PDF) . Functional Pearl. Journal of Functional Programming . 18 (4). Cambridge University Press: 423–436. CiteSeerX 10.1.1.101.4131 . doi :10.1017/s0956796808006758 (inactivo 2024-08-12). ISSN  1469-7653. S2CID  21038598. {{cite journal}}: CS1 maint: DOI inactive as of August 2024 (link)
  34. ^ Kiselyov, Oleg (mayo de 2012). Schrijvers, Tom; Thiemann, Peter (eds.). Iteratees (PDF) . Simposio internacional sobre programación funcional y lógica. Apuntes de clase en 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.
  35. ^ 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 clase en Ciencias de la Computación. Vol. 4164. Budapest, Hungría: Springer-Verlag. pp. 135–167. CiteSeerX 10.1.1.62.2047 . ISBN.  978-3-540-46845-5.
  36. ^ Uustalu, Tarmo; Vene, Varmo (junio de 2008). "Nociones comonádicas de computación". Notas electrónicas en informática teórica . 203 (5). Elsevier: 263–284. doi :10.1016/j.entcs.2008.05.029. ISSN  1571-0661.
  37. ^ Power, John; Watanabe, Hiroshi (mayo de 2002). "Combinación de una mónada y una comonada" (PDF) . Theoretical Computer Science . 280 (1–2). Elsevier: 137–162. CiteSeerX 10.1.1.35.4130 . doi :10.1016/s0304-3975(01)00024-x. ISSN  0304-3975. 
  38. ^ Gaboardi, Marco; Katsumata, Shin-ya; Orchard, Dominic; Breuvart, Flavien; Uustalu, Tarmo (septiembre de 2016). Combinación de efectos y coefectos mediante calificación (PDF) . 21.ª Conferencia internacional de la ACM sobre programación funcional. Nara, Japón: Association for Computing Machinery. págs. 476–489. doi :10.1145/2951913.2951939. ISBN . 978-1-4503-4219-3.

Enlaces externos

Referencias de HaskellWiki:

Tutoriales:

Casos interesantes: