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] Las investigaciones que comenzaron a fines de la década de 1980 y principios de la de 1990 establecieron que las mónadas podían 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 debería 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, verifica 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 a 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 producirá 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 la mónada.

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 usando 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 adició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 φ) xlista = [ φ(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]) = xlista1 ++ xlista2 ++ ... ++ xlistan

La mónada resultante no es sólo una lista, sino una que se redimensiona y condensa automáticamente a medida que se aplican las 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 tubería 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 ++ "!" ))                     

Monada del escritor (JavaScript)

Otra situación habitual es la de mantener un archivo de registro o informar de algún 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 ) >>= bar ) >>= 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 ningún patrón simple recomienda 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 comónada 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 comónada 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:

cuenta ∘ ( (wa =>> f) → wb ) ↔ f(wa) → bwa =>> cuenta ↔ wawa ( (=>> f(wx = wa)) → wb (=>> g(wy = wb)) → wc )( wa (=>> f(wx = wa)) → wb ) (=>> g(wy = wb)) → wc

De manera análoga a las mónadas, las 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 =>> (φ ∘ cont) wxduplicar wa ↔ wa =>> wx
wa =>> f(wx) ↔ ((mapa f) ∘ duplicado) wa

A simple example is the Product comonad, which outputs values based on an input value and shared environment data. In fact, the Product comonad is just the dual of the Writer monad and effectively the same as the Reader monad (both discussed below).Product and Reader differ only in which function signatures they accept, and how they complement those functions by wrapping or unwrapping values.

A less trivial example is the Stream comonad, which can be used to represent data streams and attach filters to the incoming signals with extend. In fact, while not as popular as monads, researchers have found comonads particularly useful for stream processing and modeling dataflow programming.[35][36]

Due to their strict definitions, however, one cannot simply move objects back and forth between monads and comonads. As an even higher abstraction, arrows can subsume both structures, but finding more granular ways to combine monadic and comonadic code is an active area of research.[37][38]

See also

Alternatives for modeling computations:

Related design concepts:

Generalizations of monads:

Notes

  1. ^ Due to the fact that functions on multiple free variables are common in programming, monads as described in this article are technically what category theorists would call strong monads.[3]
  2. ^ a b Specific motivation for Maybe can be found in (Hutton 2016).[7]
  3. ^ a b Hutton abstracts a bind which when given a type a that may fail, and a mapping ab that may fail, produces a result b that may fail. (Hutton, 2016)[7]
  4. ^ a b (Hutton 2016) notes that Just might denote Success, and Nothing might denote Failure.[7]
  5. ^ Semantically, M is not trivial and represents an endofunctor over the category of all well-typed values:
  6. ^ While a (parametrically polymorphic) function in programming terms, unit (often called η in category theory) is mathematically a natural transformation, which maps between functors:
  7. ^ bind, on the other hand, is not a natural transformation in category theory, but rather an extension that lifts a mapping (from values to computations) into a morphism between computations:
  8. ^ Strictly speaking, bind may not be formally associative in all contexts because it corresponds to application within lambda calculus, not mathematics. In rigorous lambda-calculus, evaluating a bind may require first wrapping the right term (when binding two monadic values) or the bind itself (between two monadic functions) in an anonymous function to still accept input from the left.[10]
  9. ^ By GHC version 7.10.1, and going forward, Haskell began enforcing Haskell's 2014 Applicative Monad proposal (AMP) which requires the insertion of 7 lines of code into any existing modules which use monads.[25]
  10. ^ These natural transformations are usually denoted as morphisms η, μ. That is: η, μ denote unit, and join respectively.
  11. ^ Some languages like Haskell even provide a pseudonym for map in other contexts called lift, along with multiple versions for different parameter counts, a detail ignored here.
  12. ^ In category theory, the Identity monad can also be viewed as emerging from adjunction of any functor with its inverse.
  13. ^ Category theory views these collection monads as adjunctions between the free functor and different functors from the category of sets to the category of monoids.
  14. ^ Here the task for the programmer is to construct an appropriate monoid, or perhaps to choose a monoid from a library.
  15. ^ The reader may wish to follow McCann's thread[6] and compare it with the Types below.
  16. ^ a b In this case, the bind has pasted in a string where previously only an integer had been; that is, the programmer has constructed an adjunction: a tuple (x,s), denoted int * string in the pseudocode § above.
  17. ^ Algebraically, the relationship between the two (non-commutative) monoid aspects resembles that of a near-semiring, and some additive monads do qualify as such. However, not all additive monads meet the distributive laws of even a near-semiring.[32]
  18. ^ In Haskell, extend is actually defined with the inputs swapped, but as currying is not used in this article, it is defined here as the exact dual of bind.

References

  1. ^ a b c d e f O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). "Monads". Real World Haskell. Sebastopol, California: O'Reilly Media. chapter 14. ISBN 978-0596514983.
  2. ^ a b Wadler, Philip (June 1990). Comprehending Monads. ACM Conference on LISP and Functional Programming. Nice, France. CiteSeerX 10.1.1.33.5381.
  3. ^ a b c Moggi, Eugenio (1991). "Notions of computation and monads" (PDF). Information and Computation. 93 (1): 55–92. CiteSeerX 10.1.1.158.5275. doi:10.1016/0890-5401(91)90052-4.
  4. ^ a b c d e Wadler, Philip (January 1992). The essence of functional programming. 19th Annual ACM Symposium on Principles of Programming Languages. Albuquerque, New Mexico. CiteSeerX 10.1.1.38.9516.
  5. ^ a b Hudak, Paul; Peterson, John; Fasel, Joseph (1999). "About Monads". A Gentle Introduction to Haskell 98. chapter 9.
  6. ^ a b C. A. McCann's answer (Jul 23 '10 at 23:39) How and why does the Haskell Cont monad work?
  7. ^ a b c Graham Hutton (2016) Programming in Haskell 2nd Edition
  8. ^ a b Beckerman, Brian (21 November 2012). "Don't fear the Monad". YouTube.
  9. ^ Spivey, Mike (1990). "A functional theory of exceptions" (PDF). Science of Computer Programming. 14 (1): 25–42. doi:10.1016/0167-6423(90)90056-J.
  10. ^ "Monad laws". HaskellWiki. haskell.org. Retrieved 14 October 2018.
  11. ^ "What a Monad is not". 7 October 2018.
  12. ^ De Meuter, Wolfgang (1997). Monads as a theoretical foundation for AOP (PDF). International Workshop on Aspect Oriented Programming at ECOOP. Jyväskylä, Finland. CiteSeerX 10.1.1.25.8262.
  13. ^ "Monad (sans metaphors)". HaskellWiki. 1 November 2009. Retrieved 24 October 2018.
  14. ^ O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). "Using Parsec". Real World Haskell. Sebastopol, California: O'Reilly Media. chapter 16. ISBN 978-0596514983.
  15. ^ Stewart, Don (17 May 2007). "Roll Your Own Window Manager: Tracking Focus with a Zipper". Control.Monad.Writer. Archived from the original on 20 February 2018. Retrieved 19 November 2018.
  16. ^ Benton, Nick (2015). "Categorical Monads and Computer Programming" (PDF). London Mathematical Society Impact150 Stories. 1. Retrieved 19 November 2018.
  17. ^ Kiselyov, Olag (2007). "Delimited Continuations in Operating Systems". Modeling and Using Context. Lecture Notes in Computer Science. Vol. 4635. Springer Berlin Heidelberg. pages 291--302. doi:10.1007/978-3-540-74255-5_22. ISBN 978-3-540-74255-5.
  18. ^ Meijer, Erik (27 March 2012). "Your Mouse is a Database". ACM Queue. 10 (3): 20–33. doi:10.1145/2168796.2169076.
  19. ^ Iverson, Kenneth (September 1987). "A dictionary of APL". APL Quote Quad. 18 (1): 5–40. doi:10.1145/36983.36984. ISSN 1088-6826. S2CID 18301178. Retrieved 19 November 2018.
  20. ^ Kleisli, Heinrich (1965). "Every standard construction is induced by a pair of adjoint functors" (PDF). Proceedings of the American Mathematical Society. 16 (3): 544–546. doi:10.1090/S0002-9939-1965-0177024-4. Retrieved 19 November 2018.
  21. ^ Peter Pepper, ed. (November 1997). The Programming Language Opal (Technical report) (5th corrected ed.). Fachbereich Informatik, Technische Universität Berlin. CiteSeerX 10.1.1.40.2748.
  22. ^ Moggi, Eugenio (June 1989). Computational lambda-calculus and monads (PDF). Fourth Annual Symposium on Logic in computer science. Pacific Grove, California. CiteSeerX 10.1.1.26.2787.
  23. ^ a b Peyton Jones, Simon L.; Wadler, Philip (January 1993). Imperative functional programming (PDF). 20th Annual ACM Symposium on Principles of Programming Languages. Charleston, South Carolina. CiteSeerX 10.1.1.53.2504.
  24. ^ Brent Yorgey Typeclassopedia
  25. ^ Stack overflow (8 Sep 2017) Defining a new monad in haskell raises no instance for Applicative
  26. ^ Brent Yorgey Monoids
  27. ^ "Applicative functor". HaskellWiki. Haskell.org. 7 May 2018. Archived from the original on 30 October 2018. Retrieved 20 November 2018.
  28. ^ a b Gibbard, Cale (30 December 2011). "Monads as containers". HaskellWiki. Haskell.org. Archived from the original on 14 December 2017. Retrieved 20 November 2018.
  29. ^ a b Piponi, Dan (7 August 2006). "You Could Have Invented Monads! (And Maybe You Already Have.)". A Neighborhood of Infinity. Archived from the original on 24 October 2018. Retrieved 16 October 2018.
  30. ^ "Some Details on F# Computation Expressions". 21 September 2007. Retrieved 9 October 2018.
  31. ^ Giles, Brett (12 August 2013). "Lifting". HaskellWiki. Haskell.org. Archived from the original on 29 January 2018. Retrieved 25 November 2018.
  32. ^ a b Rivas, Exequiel; Jaskelioff, Mauro; Schrijvers, Tom (July 2015). From monoids to near-semirings: the essence of MonadPlus and Alternative (PDF). 17th International ACM Symposium on Principles and Practice of Declarative Programming. Siena, Italy. CiteSeerX 10.1.1.703.342.
  33. ^ Swierstra, Wouter (2008). "Data types à la carte" (PDF). Functional Pearl. Journal of Functional Programming. 18 (4). Cambridge University Press: 423–436. CiteSeerX 10.1.1.101.4131. doi:10.1017/s0956796808006758 (inactive 2024-08-12). ISSN 1469-7653. S2CID 21038598.{{cite journal}}: CS1 maint: DOI inactive as of August 2024 (link)
  34. ^ Kiselyov, Oleg (May 2012). Schrijvers, Tom; Thiemann, Peter (eds.). Iteratees (PDF). International Symposium on Functional and Logic Programming. Lecture Notes in Computer Science. Vol. 7294. Kobe, Japan: Springer-Verlag. pp. 166–181. doi:10.1007/978-3-642-29822-6_15. ISBN 978-3-642-29822-6.
  35. ^ Uustalu, Tarmo; Vene, Varmo (July 2005). Horváth, Zoltán (ed.). The Essence of Dataflow Programming (PDF). First Summer School, Central European Functional Programming. Lecture Notes in Computer Science. Vol. 4164. Budapest, Hungary: Springer-Verlag. pp. 135–167. CiteSeerX 10.1.1.62.2047. ISBN 978-3-540-46845-5.
  36. ^ Uustalu, Tarmo; Vene, Varmo (June 2008). "Comonadic Notions of Computation". Electronic Notes in Theoretical Computer Science. 203 (5). Elsevier: 263–284. doi:10.1016/j.entcs.2008.05.029. ISSN 1571-0661.
  37. ^ Power, John; Watanabe, Hiroshi (May 2002). "Combining a monad and a comonad" (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 (September 2016). Combining Effects and Coeffects via Grading (PDF). 21st ACM International Conference on Functional Programming. Nara, Japan: Association for Computing Machinery. pp. 476–489. doi:10.1145/2951913.2951939. ISBN 978-1-4503-4219-3.

External links

HaskellWiki references:

Tutorials:

Interesting cases: