Articulo de referencia

Mónada (programación funcional)

En programación funcional , las mónadas son una forma de estructurar los cálculos como una secuencia de pasos, donde cada paso produce un valor, además de información adicional ...

En programación funcional , las mónadas son una forma de estructurar los cálculos como una secuencia de pasos, donde cada paso produce un valor, además de información adicional sobre el cálculo, como un posible fallo, no determinismo o un efecto secundario. Formalmente, una mónada es un constructor de tipos M equipado con dos operaciones: una que eleva un valor al contexto monádico y otra que encadena cálculos monádicos. En términos más sencillos, las mónadas pueden considerarse interfaces implementadas en constructores de tipos, que permiten que las funciones abstraigan diversas variantes de constructores de tipos que implementan la mónada (por ejemplo , , etc.). [ 1 ] [ 2 ]return:<A>(a:A)->M(A)bind:<A,B>(m_a:M(A),f:A->M(B))->M(B)OptionList

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 endofunctor con estructura adicional. [ a ] ​​[ b ] Las investigaciones iniciadas a finales de la década de 1980 y principios de la de 1990 establecieron que las mónadas podían unificar problemas aparentemente dispares de la informática bajo un modelo funcional. La teoría de categorías también proporciona algunos requisitos formales, conocidos como las leyes de las mónadas , que deben ser satisfechas por cualquier mónada y que pueden usarse para verificar el código monádico. [ 3 ] [ 4 ]

Dado que las mónadas explicitan la semántica para un tipo de computación, también pueden utilizarse para implementar características de lenguaje convenientes. Algunos lenguajes, como Haskell , incluso ofrecen definiciones predefinidas en sus bibliotecas principales para la estructura general de las mónadas 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 precisamente, una mónada puede usarse cuando el acceso sin restricciones a un valor resulta inapropiado por razones específicas del escenario. En el caso de la mónada Maybe, se debe a que el valor podría no existir. En el caso de la mónada de entrada/salida (E/S), se debe a que el valor aún podría desconocerse, como cuando la mónada representa la entrada del usuario, que solo se proporcionará después de que se muestre una solicitud. En todos los casos, los escenarios en los que el acceso tiene sentido se contemplan en 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 E/S, un valor se enlaza solo después de que se hayan realizado las operaciones previas en la secuencia.

Una mónada se puede crear definiendo un constructor de tipo M y dos operaciones:

  • return :: a -> M a(a menudo también llamada unidad ), que recibe un valor de tipo ay lo envuelve en un valor monádico de tipo M ay
  • bind :: (M a) -> (a -> M b) -> (M b)(normalmente representado como >>=), que recibe un valor monádico de tipo M ay una función fque acepta valores del tipo base a. Bind desenvuelve M a, fle aplica y puede procesar el resultado de fcomo un valor monádico M b.

(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 a partir de functores ).

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

Por lo general, el operador de enlace >>=puede contener código exclusivo de la mónada que realiza pasos de cálculo adicionales no 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 transmitirla a lo largo de la tubería. También puede ejercer un control más preciso del flujo de ejecución, por ejemplo, llamando a la función solo bajo ciertas condiciones o ejecutando las llamadas a funciones en un orden específico.

Un ejemplo: Quizás

Un ejemplo de mónada es el Maybetipo. Los resultados nulos indefinidos son un problema particular que muchos lenguajes procedimentales no proporcionan herramientas específicas para abordar, lo que requiere el uso del patrón de objeto nulo o comprobaciones para detectar valores no válidos en cada operación para manejar valores indefinidos. Esto causa errores y dificulta la creación de software robusto que gestione los errores de forma adecuada. 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 sintáctico, que debe devolver un resultado intermedio o bien señalar una condición que el analizador sintáctico ha detectado y que el programador también debe manejar. Con un poco de funcionalidad adicional, este Maybetipo se transforma en una mónada completa. [ c ] : 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 el lenguaje de programación Rust se llama Option<T>y las variantes de este tipo pueden ser un valor de tipo genéricoT o la variante vacía: None.

// El <T> representa un tipo genérico "T" enum Option < T > { Some ( T ), None , }

Option<T>También puede entenderse como un tipo "envoltorio", y aquí es donde entra en juego su conexión con las mónadas. En lenguajes que incluyen alguna variante del tipo Maybe, existen funciones que facilitan su uso, como la composición de funciones monádicas y la comprobación de si un Maybe contiene un valor.

En el siguiente ejemplo codificado, se utiliza un tipo Maybe 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 ) -> Option < Decimal > { if y == 0 { None } else { Some ( x / y ) } } // divide(1.0, 4.0) -> devuelve Some(0.25) // divide(3.0, 0.0) -> devuelve None

Una forma de comprobar si un Maybe contiene un valor es mediante el uso ifde sentencias.

let m_x = divide ( 3.14 , 0.0 ); // ver la función divide arriba // La instrucción if extrae x de m_x si m_x es la variante Just de Maybe if let Some ( 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.

let result = divide ( 3.0 , 2.0 ); match result { Some ( x ) => println! ( "Respuesta: {}" , x ), None => println! ( "La división falló; lo lograremos la próxima vez." ), }

Las mónadas pueden componer funciones que devuelven Maybe, combinándolas. Un ejemplo concreto podría ser una función que recibe varios parámetros Maybe y devuelve un único Maybe cuyo valor es Nothing cuando alguno de los parámetros es Nothing, como en el siguiente ejemplo:

fn chainable_division ( maybe_x : Option < Decimal > , maybe_y : Option < Decimal > ) -> Option < Decimal > { let x = maybe_x ? ; let y = maybe_y ? ;// Si ambas entradas no son None, // comprueba si hay división por cero y divide en consecuencia // de lo contrario, devuelve None if y == 0 { None } else { Some ( x / y ) } } chainable_division ( chainable_division ( Some ( 2.0 ), Some ( 0.0 )), Some ( 1.0 )); // dentro de chainable_division falla, fuera de chainable_division devuelve None

En lugar de repetir Someexpresiones, podemos usar un operador llamado 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 sobre el valor interno de la mónada pasada, devolviendo la mónada resultante.

// Ejemplo de Rust usando ".map". maybe_x se pasa a través de 2 funciones que devuelven Decimal y String respectivamente. // Como con la composición de funciones normal, las entradas y salidas de las funciones que se alimentan entre sí deben coincidir con los tipos envueltos. (es decir, la función add_one debe devolver un Decimal que luego se puede pasar a la función decimal_to_string) let maybe_x : Option < Decimal > = Some ( 1.0 ); let maybe_result = maybe_x . map ( add_one ). map ( decimal_to_string );

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

mitad :: Int -> Quizás Int mitad x | par x = Just ( x ` div ` 2 ) | impar x = Nothing -- Este código divide x por la mitad dos veces. Se evalúa a Nothing si x no es un múltiplo de 4 mitad x >>= mitad

Con la >>=opción disponible, chainable_divisionse puede expresar de forma mucho más concisa 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. [ e ] : 93

chainable_division ( mx , my ) = mx >>= ( λx -> my >>= ( λy -> Just ( x / y )) )

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

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

Estos son los tres elementos necesarios 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, ambas construcciones resultan ser matemáticamente equivalentes, por lo que cualquiera de las definiciones dará como resultado una mónada válida. Dados dos tipos básicos bien definidos T y U , una mónada consta de tres partes:

  • Un constructor de tipos M que construye un tipo monádico MT [ f ]
  • Un convertidor de tipos , a menudo llamado unidad o retorno , que incrusta un objeto x en la mónada:
    unit : T → M T[ g ]
  • Un combinador , normalmente llamado bind (como en bind a variable ) y representado con un operador infijo>>= o un método llamado flatMap , que desenvuelve una variable monádica y luego la inserta en una función/expresión monádica, dando como resultado un nuevo valor monádico:
    (>>=) : (M T, T → M U) → M U[ h ] entonces siy, entoncesma : M Tf : T → M U (ma >>= f) : M U

Para ser consideradas plenamente como una mónada, estas tres partes también deben respetar algunas leyes:

  • La unidad es una identidad izquierda para bind :
    unit(x) >>= ff(x)
  • La unidad también es una identidad derecha para bind :
    ma >>= unitma
  • bind es esencialmente asociativo : [ i ]
    ma >>= λx → (f(x) >>= g)(ma >>= f) >>= g[ 1 ]

Algebraicamente, esto significa que cualquier mónada da lugar tanto a una categoría (llamada categoría de Kleisli ) como a un monoide en la categoría de functores (de valores a computaciones), 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 con el razonamiento matemático. Independientemente del 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 reificar un tipo específico de computación, una mónada no solo encapsula los tediosos detalles de ese patrón computacional, sino que lo hace de forma declarativa , mejorando la claridad del código. Como los valores monádicos representan explícitamente no solo valores calculados, sino también efectos calculados , una expresión monádica puede reemplazarse por su valor en posiciones referencialmente transparentes , al igual que las expresiones puras, lo que permite muchas técnicas y optimizaciones basadas en la reescritura . [ 4 ]

Normalmente, los programadores utilizan `bind` para encadenar funciones monádicas en una secuencia, lo que ha llevado a algunos a describir las mónadas como "puntos y comas programables", en referencia a cómo muchos lenguajes imperativos utilizan puntos y comas para separar sentencias . [ 1 ] [ 5 ] Sin embargo, las mónadas no ordenan realmente los cálculos; incluso en lenguajes que las utilizan como características centrales, una composición de funciones más simple puede organizar los pasos dentro de un programa. La utilidad general de una mónada reside más bien en simplificar la estructura de un programa y mejorar la separación de responsabilidades mediante la abstracción. [ 4 ] [ 11 ]

La estructura de mónada también puede considerarse una variación matemática y en tiempo de compilación única del patrón decorador . Algunas mónadas pueden transmitir datos adicionales inaccesibles para las funciones, e incluso algunas ejercen un control más preciso sobre la ejecución, por ejemplo, llamando a una función solo bajo ciertas condiciones. Dado que permiten a los programadores de aplicaciones implementar la lógica de dominio mientras delegan el código repetitivo a módulos predesarrollados, las mónadas pueden incluso considerarse una herramienta para la programación orientada a aspectos . [ 12 ]

Otro uso destacable 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, mediante una intrincada combinación de composición de funciones y, en particular , el estilo de paso de continuaciones (CPS). [ 2 ] Sin embargo, con las mónadas, gran parte de esta estructura 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 de forma predeterminada, aún es posible implementar el patrón, a menudo sin mucha dificultad. Al traducirlo de la teoría de categorías a términos de programación, la estructura de mónada es un concepto genérico y puede definirse directamente en cualquier lenguaje que admita una característica equivalente para el polimorfismo acotado . La capacidad de un concepto para ser independiente de los detalles operacionales mientras trabaja con tipos subyacentes es poderosa, pero las características únicas y el comportamiento riguroso de las mónadas las distinguen de otros conceptos. [ 13 ]

Aplicaciones

Los análisis de mónadas específicas suelen centrarse en la solución de un problema de implementación concreto, ya que cada mónada representa una forma computacional específica. Sin embargo, en algunos casos, una aplicación puede incluso alcanzar sus objetivos generales utilizando mónadas apropiadas en su lógica central.

Aquí presentamos algunas aplicaciones que tienen las mónadas como elemento central de su diseño:

Historia

El término "mónada" en programación se remonta a los lenguajes de programación APL y J , que tienden a ser puramente funcionales. Sin embargo, en esos lenguajes, "mónada" es solo una abreviatura para 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 (denominándola "construcción estándar") a finales de la década de 1950, aunque el término "mónada" que llegó a predominar fue popularizado por el teórico de categorías Saunders Mac Lane . 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 functores (covariantes). [ 20 ]

A partir de la década de 1980, comenzó a surgir en la comunidad de la informática una vaga noción del patrón mónada. Según el investigador de lenguajes de programación Philip Wadler , el informático John C. Reynolds anticipó varias facetas del mismo en las décadas de 1970 y principios de 1980, cuando analizó el valor del estilo de paso de continuaciones , de la teoría de categorías como una rica fuente para la semántica formal y de la distinción de tipos entre valores y computaciones. [ 4 ] El lenguaje de investigación Opal , que se diseñó activamente hasta 1990, también basaba efectivamente la E/S en un tipo monádico, pero la conexión no se comprendió 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 presentado en una conferencia en 1989, [ 22 ] seguido de una presentación más elaborada en una revista en 1991. En trabajos anteriores, varios científicos informáticos habían avanzado utilizando la teoría de categorías para proporcionar semántica al cálculo lambda . La idea clave de Moggi fue que un programa del mundo real no es solo una función de valores a otros valores, sino más bien una transformación que forma cálculos sobre esos valores. Cuando se formaliza en términos de teoría de categorías, esto lleva a la conclusión de que las mónadas son la estructura para representar estos cálculos. [ 3 ]

Varios otros popularizaron y desarrollaron esta idea, incluidos Philip Wadler y Simon Peyton Jones , ambos involucrados en la especificación de Haskell. En particular, Haskell utilizó un problemático modelo de "flujo perezoso" hasta la versión 1.2 para conciliar la E/S con la evaluación perezosa , hasta que cambió a una interfaz monádica más flexible. [ 23 ] La comunidad de Haskell aplicaría mónadas a muchos problemas en programación funcional, y en la década de 2010, los investigadores que trabajaban con Haskell finalmente reconocieron que las mónadas son functores aplicativos ; [ 24 ] [ j ] y que tanto las mónadas como las flechas son monoides . [ 26 ]

Inicialmente, la programación con mónadas se limitaba principalmente a Haskell y sus derivados, pero a medida que la programación funcional ha influido en otros paradigmas, muchos lenguajes han incorporado un patrón de mónada (en esencia, si no en nombre). Actualmente existen formulaciones en Scheme , Perl , Python , Racket , Clojure , Scala y F# , y también se han considerado para un nuevo estándar de ML .

Análisis

Una de las ventajas del patrón mónada es que aporta precisión matemática a la composición de cálculos. Las leyes de las mónadas se pueden usar para comprobar la validez de una instancia, además de que las características de estructuras relacionadas (como los functores) se pueden usar mediante subtipado .

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 cumpliera las leyes de las mónadas.

Esto se puede rectificar introduciendo los detalles específicos 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 a sino o Nada ⇔ Nada fin 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 sino sino Nada Nada fin si fin sisi ma es (Solo a) y f(a) es (Solo b) entonces (g ∘ f) a sino si ma es (Solo a) y f(a) es Nada entonces Nada sino Nada fin si

Derivación a partir de functores

Aunque es menos común en informática, se puede usar directamente la teoría de categorías, que define una mónada como un functor con dos transformaciones naturales añadidas . [ k ] Así pues, para empezar, una estructura requiere una función de orden superior (o "funcional") llamada map para calificar como functor:

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

Sin embargo, esto no siempre representa un problema importante, especialmente cuando una mónada se deriva de un functor preexistente, en cuyo caso la mónada hereda automáticamente la propiedad map . (Por razones históricas, en Haskell mapse utiliza otro nombre ).fmap

La primera transformación de una mónada es en realidad la misma unidad de la tripleta de Kleisli, pero siguiendo de cerca la jerarquía de estructuras, resulta que la unidad caracteriza un functor aplicativo , una estructura intermedia entre una mónada y un functor básico. En el contexto aplicativo, a veces se hace referencia a la unidad como pura , pero sigue siendo la misma función. Lo que difiere en esta construcción es la ley que debe satisfacer la unidad ; como `bind` no está definido, la restricción se da en términos de `map` :

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

El salto final del functor aplicativo a la mónada se produce con la segunda transformación, la función de unión (en teoría de categorías esta es una transformación natural que se suele llamar μ ), 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 una tripleta de Kleisli, la estructura subyacente será la misma y las formas se pueden derivar fácilmente unas de otras:

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

Otro ejemplo: Lista

La mónada List demuestra de forma natural lo útil que puede resultar derivar una mónada a partir de un functor más simple. En muchos lenguajes, la estructura de una lista viene predefinida junto con algunas características básicas, por lo que se asume que el Listconstructor de tipos y el operador append (representado con ++para la notación infija) ya están dados aquí.

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

unidad(x) = [x]

Desde esta perspectiva, aplicar una función de forma iterativa con una comprensión de lista puede parecer una opción sencilla para enlazar y convertir listas en una mónada completa. La dificultad de este enfoque radica en que `bind` espera funciones monádicas, que en este caso generarán listas; a medida que se aplican 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, es decir, sobre un mapa , es sencillo:

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

Ahora bien, estos dos procedimientos ya dan lugar Lista un functor aplicativo. Para que se considere una mónada, solo se necesita una noción correcta de unión para aplanar la estructura repetida, pero para las listas, eso simplemente significa desenvolver una lista externa para añadir las internas que contienen valores:

unir(listax) = unir([listax1, listax2, ..., listaxn]) = listax1 ++ listax2 ++ ... ++ listaxn

La mónada resultante no es solo una lista, sino una que se redimensiona y condensa automáticamente a medida que se aplican funciones. Ahora también se puede derivar `bind`List con solo una fórmula, y luego usarla para pasar valores a través de una secuencia de funciones monádicas:

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

Una aplicación de esta lista monádica es la representación de la computación no determinista . ListPuede almacenar los resultados de todas las rutas de ejecución de un algoritmo y luego condensarse en cada paso para "olvidar" qué rutas condujeron a qué resultados (una distinción a veces importante con respecto a los algoritmos deterministas y exhaustivos). Otra ventaja es que se pueden incorporar comprobaciones en la mónada; las rutas específicas se pueden podar de forma transparente en su primer punto de fallo, sin necesidad de reescribir las 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 finales m•n deberían ser idénticos al resultado 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 de los programas. 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ácticonotación do

Aunque usar `bind` abiertamente suele tener sentido, muchos programadores prefieren una sintaxis que imite las sentencias imperativas (llamada notación `do` en Haskell, notación `perform` en OCaml , expresiones de computación en F# [ 30 ] y comprensión en Scala ). Esto es solo azúcar sintáctico que disfraza una secuencia monádica como un bloque de código ; el compilador luego traduce automáticamente estas expresiones al código funcional subyacente.

Traducir 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 mi = caso mx de Nada -> Nada Solo x -> caso mi de Nada -> Nada Solo y -> Solo ( x + y )

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

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

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

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

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

let readNum ( ) = let s = Console.ReadLine ( ) let succ , v = Int32.TryParse ( s ) if ( succ ) then Some ( v ) else Nonelet secure_div = maybe { let ! x = readNum () let ! y = readNum () if ( y = 0 ) then None else return ( x / y ) }

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

tal vez . Retraso ( fun () -> tal vez . Enlace ( readNum () , fun x -> tal vez . Enlace ( readNum () , fun y -> if ( y = 0 ) then None else tal vez . Retorno ( x / y ))))

Por último ejemplo, incluso las leyes generales de las mónadas pueden expresarse en notación do:

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

Interfaz general

Cada mónada requiere una implementación específica que cumpla con sus leyes, pero otros aspectos, como la relación con otras estructuras o las convenciones estándar de un lenguaje, son comunes a todas las mónadas. Por lo tanto, un lenguaje o biblioteca puede proporcionar una Monadinterfaz general con prototipos de funciones , relaciones de subtipo y otros datos generales. Además de facilitar el desarrollo y garantizar que una nueva mónada herede características de un supertipo (como los functores), verificar el diseño de una mónada con respecto a la interfaz añade un nivel adicional de control de calidad.

Operadores

El código monádico a menudo se puede simplificar aún más mediante el uso juicioso de operadores. El funcional map puede ser especialmente útil, ya que funciona con más que solo funciones monádicas ad hoc; siempre que una función monádica deba funcionar de forma análoga a un operador predefinido, map se puede usar para " elevar " instantáneamente el operador más simple a uno monádico. [ l ] Con esta técnica, la definición adddel Maybeejemplo podría destilarse en:

agregar(mx,my) = map (+)

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

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

Otro operador monádico que también resulta útil para el análisis es la composición monádica (representada >=>aquí como infijo), que permite encadenar funciones monádicas de una manera más matemática:

(f >>> g)(x) = f(x) >>= g

Con este operador, las leyes de la mónada pueden escribirse únicamente 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 = T unidad(x) = x (x >>= f) = f(x)

IdentitySin embargo, sí tiene usos válidos, como proporcionar un caso base para transformadores de mónadas recursivos . También se puede utilizar para realizar asignaciones básicas de variables dentro de un bloque de estilo imperativo. [ m ]

Colecciones

Cualquier colección con una operación append adecuada ya es un monoide, pero resulta que Listno es la única colección que también tiene una operación join bien definida y califica como mónada. Incluso se puede mutar Lista estas otras colecciones monádicas simplemente imponiendo propiedades especiales a append : [ n ] [ o ]

mónada IO (Haskell)

Como ya se mencionó, el código puro no debería tener efectos secundarios no gestionados, pero eso no impide que un programa describa y gestione explícitamente los efectos. Esta idea es fundamental para la mónada IO de Haskell , donde un objeto de tipo IO apuede verse como la 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 basándose en la información sobre el mundo proporcionada por la acción anterior (entrada de usuarios, archivos, etc.). [ 23 ] Lo más significativo es que, dado que el valor de la mónada IO solo puede vincularse a una función que calcula otra mónada IO, la función de vinculación impone una disciplina de una secuencia de acciones donde el resultado de una acción solo puede proporcionarse a una función que calculará la siguiente acción a realizar. Esto significa que las acciones que no necesitan realizarse nunca se realizan, y las acciones que sí necesitan realizarse tienen una secuencia bien definida.

Por ejemplo, Haskell tiene varias funciones para operar en el sistema de archivos en general , incluyendo una que verifica si un archivo existe y otra que elimina un archivo. Sus dos firmas de tipo son:

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

La primera función se interesa en comprobar si un archivo determinado existe realmente y, como resultado, devuelve un valor booleano dentro de la IOmónada. La segunda función, en cambio, solo se ocupa de actuar sobre el sistema de archivos, de modo que el IOcontenedor que devuelve esté vacío.

IOSin embargo, no se limita solo a la entrada/salida de archivos; incluso permite la entrada/salida de usuarios y, junto con una sintaxis imperativa simplificada, 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 ( "Encantado de conocerte, " ++ name ++ "!" )

Sin azúcar, esto se traduce en la siguiente secuencia monádica ( >>en Haskell es solo una variante de bind para cuando solo importan los efectos monádicos y el resultado subyacente puede descartarse):

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

Mónada escritora (Java)

Otra situación común es mantener un archivo de registro o informar sobre el progreso de un programa. A veces, un programador puede querer registrar datos técnicos aún más específicos para su posterior análisis o depuración . La mónada Writer puede gestionar estas tareas generando una salida auxiliar que se acumula paso a paso.

Para demostrar que el patrón mónada no se limita principalmente a lenguajes funcionales, este ejemplo implementa una Writermónada en Java , almacenada en una clase que representa la Writermónada.

import java.util.ArrayList ; import java.util.List ; import java.util.function.Function ;Escritor de registro < T > ( T valor , Lista < Cadena > registro ) { // Internos aquí... }

Definir la unidad también es muy sencillo:

Escritor de registros < T > ( T valor , Lista < Cadena > registro ) { // ...public static < T > Writer < T > unit ( T value ) { return new Writer <> ( value , new ArrayList <> ()); } }

Solo se necesita una unidad para definir funciones simples que generen Writerobjetos con notas de depuración:

import java.util.List ;clase Ops { privado Ops () {}public static Writer < Integer > squared ( int x ) { return new Writer <> ( x * x , List . of ( String . format ( "%d fue elevado al cuadrado." , x ))); }public static Writer < Integer > halved ( int x ) { return new Writer <> ( x / 2 , List . of ( String . format ( "%d se dividió por la mitad." , x ))); } }

Una mónada verdadera todavía requiere enlace , pero para Writer, esta operación combina el registro actual con el registro producido al aplicar una transformación:

Escritor de registros < T > ( T valor , Lista < Cadena > registro ) { // ...public < U > Writer < U > bind ( Function < T , Writer < U >> transform ) { Writer < U > result = transform . apply ( this . value ); List < String > newLog = new ArrayList <> ( this . log ); newLog . addAll ( result . log ()); return new Writer <> ( result . value (), newLog ); } }

Ahora se pueden encadenar las funciones de ejemplo utilizando `bind` , lo que se puede representar mediante el encadenamiento de métodos :

Writer < Integer > result = Writer . unit ( 4 ) . bind ( Ops :: squared ) . bind ( Ops :: halved );

El resultado final es una clara separación de responsabilidades entre la realización de cálculos y la acumulación de registros de salida para su posterior inspección:

System.out.println ( result.value ()); // 8 System.out.println( result.log ( ) ) ; // [ 4 se elevó al cuadrado , 16 se dividió por la mitad. ]

mónada ambiental

Una mónada de entorno (también llamada mónada lectora o 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 la mónada son: devolver:TmiT=tmitunir:(miT)(TmiT)miT=rFmiF(rmi)mi{\displaystyle {\begin{array}{ll}{\text{return}}\colon &T\rightarrow E\rightarrow T=t\mapsto e\mapsto t\\{\text{bind}}\colon &(E\rightarrow T)\rightarrow (T\rightarrow E\rightarrow T')\rightarrow E\rightarrow T'=r\mapsto f\mapsto e\mapsto f\,(r\,e)\,e\end{array}}}

Las siguientes operaciones monádicas son útiles: preguntar:mimi=identificaciónmilocal:(mimi)(miT)miT=Fdomido(Fmi){\displaystyle {\begin{array}{ll}{\text{preguntar}}\colon &E\rightarrow E={\text{id}}_{E}\\{\text{local}}\colon &(E\rightarrow E)\rightarrow (E\rightarrow T)\rightarrow E\rightarrow T=f\mapsto c\mapsto e\mapsto c\,(f\,e)\end{array}}}

La operación `ask` se utiliza para recuperar el contexto actual, mientras que `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 añadido; return y bind son equivalentes a los combinadores K y S , respectivamente, en el cálculo de combinadores SKI .

mónadas de estado

Una mónada de estado permite al programador asociar 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, lo que 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 la 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 de estado útiles incluyen:

obtener = \ s -> ( s , s ) -- Examinar el estado en este punto del cálculo. poner s = \ _ -> ( () , s ) -- Reemplazar el estado. modificar f = \ s -> ( () , f s ) -- Actualizar el estado

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

runState :: Estado s a -> s -> ( a , s ) runState t s = t s

Los bloques do en una mónada de estado son secuencias de operaciones que pueden examinar y actualizar los datos de estado.

De manera informal, una mónada de estado de tipo de estado S asigna el tipo de valores de retorno T a funciones de tipoST×S{\displaystyle S\rightarrow T\times S}donde S es el estado subyacente. Las funciones de retorno y enlace son:

devolver:TST×S=ts(t,s)unir:(ST×S)(TST×S)ST×S =metroks(k t s)dónde(t,s)=metros{\displaystyle {\begin{array}{ll}{\text{return}}\colon &T\rightarrow S\rightarrow T\times S=t\mapsto s\mapsto (t,s)\\{\text{bind}}\colon &(S\rightarrow T\times S)\rightarrow (T\rightarrow S\rightarrow T'\times S)\rightarrow S\rightarrow T'\times S\ =m\mapsto k\mapsto s\mapsto (k\ t\ s')\quad {\text{where}}\;(t,s')=m\,s\end{array}}}.

Desde el punto de vista de la teoría de categorías, una mónada de estado se deriva de la adjunción entre el functor producto y el functor exponencial, que existe en cualquier categoría cartesiana cerrada por definición.

mónada de continuación

Una mónada de continuación [ p ] con tipo de retorno R asigna el tipo T a funciones de tipo(TR)R{\displaystyle \left(T\rightarrow R\right)\rightarrow R}Se utiliza para modelar el estilo de paso de continuaciones . Las funciones de retorno y enlace son las siguientes:

devolver:T(TR)R=tFFtunir:((TR)R)(T(TR)R)(TR)R=doFkdo(tFtk){\displaystyle {\begin{array}{ll}{\text{return}}\colon &T\rightarrow \left(T\rightarrow R\right)\rightarrow R=t\mapsto f\mapsto f\,t\\{\text{bind}}\colon &\left(\left(T\rightarrow R\right)\rightarrow R\right)\rightarrow \left(T\rightarrow \left(T'\rightarrow R\right)\rightarrow R\right)\rightarrow \left(T'\rightarrow R\right)\rightarrow R=c\mapsto f\mapsto k\mapsto c\,\left(t\mapsto f\,t\,k\right)\end{array}}}

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

llamar/cc: ((T(TR)R)(TR)R)(TR)R=Fk(F(tincógnitakt)k){\displaystyle {\text{call/cc}}\colon \ \left(\left(T\rightarrow \left(T'\rightarrow R\right)\rightarrow R\right)\rightarrow \left(T\rightarrow R\right)\rightarrow R\right)\rightarrow \left(T\rightarrow R\right)\rightarrow R=f\mapsto k\mapsto \left(f\left(t\mapsto x\mapsto k\,t\right)\,k\right)}

Registro del programa

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 reciben un número entero y devuelven otro. Entonces podemos aplicar las funciones sucesivamente 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. Entonces cambiamos los tipos de la siguiente manera:

foo : int -> int * string bar : int -> int * string

De modo que ambas funciones devuelvan una tupla, con el resultado de la aplicación como número entero y un mensaje de registro con información sobre la función aplicada y todas las funciones aplicadas previamente como cadena de texto.

Desafortunadamente, esto significa que ya no podemos componerfoo y bar, ya que su tipo de entrada intno es compatible con su tipo de salida int * string. Y aunque podemos recuperar la capacidad de composición modificando los tipos de cada función a int * string -> int * string, esto requeriría agregar código repetitivo a cada función para extraer el entero de la tupla, lo cual se volvería tedioso a medida que aumenta el número de dichas funciones.

En su lugar, definamos una función auxiliar para abstraer este código repetitivo por nosotros:

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

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

Ahora hemos recuperado cierta capacidad de composición. Por ejemplo:

enlazar ( enlazar ( x , s ) barra ) foo

Donde (x,s)es una tupla de entero y cadena. [ q ]

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

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

Eso t >>= fes lo mismo que bind t f.

Entonces, el ejemplo anterior se convierte en:

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

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

retorno : int -> int * cadena

Lo cual se envuelve xen la tupla descrita anteriormente.

El resultado es un sistema para registrar mensajes:

(( return x ) >>= bar ) >>= foo

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

int * stringdenota un valor monádico pseudocodificado . [ q ]bind y returnson análogos a las funciones correspondientes del mismo nombre. 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 añadido mplus y un elemento identidad bajo mplus , llamado mzero . La Maybemónada puede considerarse aditiva, con Nothingcomo mzero 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 vincule a una función monádica. Esta propiedad es bidireccional, y bind también devolverá mzero cuando cualquier valor se vincule a una función monádica cero .

En términos de teoría de categorías, una mónada aditiva califica una vez como un monoide sobre funciones monádicas con bind (como todas las mónadas), y nuevamente sobre valores monádicos a través de mplus . [ 32 ] [ r ]

mónadas libres

A veces, el esquema general de una mónada puede ser útil, pero ningún patrón sencillo recomienda una mónada en particular. Aquí es donde entra en juego una mónada libre ; como objeto libre en la categoría de mónadas, puede representar la estructura monádica sin restricciones específicas más allá de las propias leyes de la mónada. Del mismo modo que 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 no impone ninguna semántica más profunda.

Por ejemplo, al trabajar completamente a través de los marcadores Justy , la mónada es una mónada libre. La mónada, en cambio, no es una mónada libre ya que incorpora hechos adicionales y específicos sobre las listas (como append ) a su definición. Un último ejemplo es una mónada libre abstracta:NothingMaybeList

datos Free f a = Pure a | Free ( f ( Free f a ))unidad :: a -> Libre f una unidad x = x purobind :: 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 puede usarse para rastrear la sintaxis y el tipo, dejando la semántica para más adelante, y como resultado, se ha utilizado en analizadores sintácticos e intérpretes . [ 33 ] Otros también las han aplicado a problemas operacionales 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 a valores. El código monádico, en cierto sentido, no puede "desempaquetarse" por completo; una vez que un valor se encapsula dentro de una mónada, permanece aislado allí junto con cualquier efecto secundario (algo positivo en la programación puramente funcional). Sin embargo, a veces el problema radica más en 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, en términos generales, que tendrá los mismos componentes requeridos, solo que con la dirección de las firmas de tipo invertida . Partiendo de la definición de mónada centrada en el enlace , una comónada consta de:

  • Un constructor de tipos W que marca el tipo de orden superior WT
  • El dual de la unidad , llamado aquí counidad , extrae el valor subyacente de la comónada:
counit(wa) : WT → T
  • Una inversión de bind (también representada con =>>) que extiende una cadena de funciones reductoras:
(wa =>> f) : (WU, WU → T) → WT [ s ]

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

unidad ∘ ( (wa =>> f) → wb ) ↔ f(wa) → b wa =>> counit ↔ wa wa ( (=>> f(wx = wa)) → wb (=>> g(wy = wb)) → wc )( wa (=>> f(wx = wa)) → wb ) (=>> g(wy = wb)) → wc

De forma análoga a las mónadas, las comónadas también pueden derivarse de functores utilizando un dual de join :

  • La función `duplicate` toma un valor que ya existe como un valor comonádico y lo envuelve en otra capa de estructura comonádica:
duplicar(wa) : WT → W (WT)

Sin embargo, si bien 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 functores con `map` , no cofunctores . 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) wx duplicar wa ↔ wa =>> wx
wa =>> f(wx) ↔ ((mapa f) ∘ duplicado) wa

Un ejemplo sencillo es la comónada Product , que genera valores a partir de un valor de entrada y datos de entorno compartidos. La Productcomónada es solo el dual de la Writermónada y, en la práctica, es igual a Readerella (ambas se analizan más adelante). ProductSe Readerdiferencian únicamente en las firmas de función que aceptan y en cómo complementan dichas funciones mediante el encapsulado o desencapsulado de valores.

Un ejemplo menos trivial es la comónada Stream , que se puede usar para representar flujos de datos y adjuntar filtros a las señales entrantes con extend . Si bien no son tan populares como las mónadas, los investigadores han encontrado que las comónadas son particularmente útiles para el procesamiento de flujos y la programación de modelado de flujo de datos . [ 35 ] [ 36 ]

Sin embargo, debido a sus definiciones estrictas, no es posible simplemente mover objetos entre mónadas y comónadas. Como un nivel de abstracción aún mayor, las flechas pueden englobar ambas estructuras, pero encontrar formas más precisas de combinar el código monádico y el comónado es un área de investigación activa. [ 37 ] [ 38 ]

Véase también

Alternativas para modelar cálculos:

  • Los sistemas de efectos (en particular, los manejadores de efectos algebraicos) son una forma diferente de describir los efectos secundarios como tipos.
  • Los tipos de unicidad son un tercer enfoque para manejar los efectos secundarios en los lenguajes funcionales.

Conceptos de diseño relacionados:

  • La programación orientada a aspectos enfatiza la separación del código auxiliar de contabilidad para mejorar la modularidad y la simplicidad.
  • La inversión de control es el principio abstracto de llamar a funciones específicas desde un marco general.
  • Las clases de tipos son una característica específica del lenguaje que se utiliza para implementar mónadas y otras estructuras en Haskell.
  • El patrón decorador es una forma más concreta y ad hoc de lograr beneficios similares en la programación orientada a objetos.

Generalizaciones de las mónadas:

  • Los functores aplicativos generalizan a partir de las mónadas conservando únicamente la unidad y las leyes que la relacionan con el mapa.
  • Las flechas utilizan una estructura adicional para agrupar funciones simples y mónadas bajo una única interfaz.
  • Los transformadores de mónadas actúan sobre mónadas distintas para combinarlas modularmente.

Notas

  1. Más formalmente, una mónada es un monoide en la categoría de endofuntores .
  2. Debido a que las funciones sobre 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 ]
  3. 1 2 La motivación específica para Maybe se puede encontrar en (Hutton 2016). [ 7 ]
  4. 1 2 Hutton abstrae abindque, dado un tipo a que puede fallar y un mapeo a b que puede fallar, produce un resultado b que puede fallar. (Hutton, 2016) [ 7 ]
  5. 1 2 (Hutton 2016) señala que Just podría denotar éxito, y Nothing podría denotar fracaso. [ 7 ]
  6. Semánticamente, M no es trivial y representa un endofunctor sobre la categoría de todos los valores bien tipados:METRO:ValVal{\displaystyle M:{\mathit {Val}}\to {\mathit {Val}}}
  7. Si bien una función (paramétricamente polimórfica) en términos de programación, la unidad (a menudo llamada η en teoría de categorías) es matemáticamente una transformación natural que mapea entre functores :ηA:id(ValA)METRO(ValA){\displaystyle \eta _{A}:\mathrm {id} ({\mathit {Val}}_{A})\to M({\mathit {Val}}_{A})}
  8. bind , en cambio, no es una transformación natural en la teoría de categorías, sino más bien una extensión.{\displaystyle -^{*}}que eleva una correspondencia (de valores a cálculos) a un morfismo entre cálculos:F:ValAMETRO(ValB),F:METRO(ValA)METRO(ValB){\displaystyle \forall f:{\mathit {Val}}_{A}\to M({\mathit {Val}}_{B}),f^{*}:M({\mathit {Val}}_{A})\to M({\mathit {Val}}_{B})}
  9. Estrictamente hablando, `bind` 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 `bind` puede requerir primero envolver el término derecho (cuando se enlazan dos valores monádicos) o el propio `bind` (entre dos funciones monádicas) en una función anónima para seguir aceptando la entrada de la izquierda. [ 10 ]
  10. A partir de la versión 7.10.1 de GHC, y en adelante, Haskell comenzó a aplicar la propuesta de mónada aplicativa (AMP) de Haskell de 2014, que requiere la inserción de 7 líneas de código en cualquier módulo existente que utilice mónadas. [ 25 ]
  11. Estas transformaciones naturales se suelen denotar como morfismos η, μ. Es decir: η, μ denotan unidad y unión respectivamente.
  12. Algunos lenguajes como Haskell incluso proporcionan un seudónimo para map en otros contextos llamadolift, junto con múltiples versiones para diferentes cantidades de parámetros, un detalle que se ignora aquí.
  13. En teoría de categorías, laIdentitymónada también puede verse como surgida de la adjunción de cualquier functor con su inverso.
  14. La teoría de categorías considera estas mónadas de colección como adjunciones entre el functor libre y diferentes functores de la categoría de conjuntos a la categoría de monoides .
  15. Aquí la tarea del programador es construir un monoide apropiado, o tal vez elegir un monoide de una biblioteca.
  16. El lector puede querer seguir el hilo de McCann [ 6 ] y compararlo con los Tipos que aparecen a continuación.
  17. 1 2 En este caso, sebindha pegado unstringdonde anteriormente solointegerhabía un; es decir, el programador ha construido una adjunción : una tupla(x,s), denotadaint * stringen el pseudocódigo § anterior .
  18. Algebraicamente, la relación entre los dos aspectos monoides (no conmutativos) se asemeja a la de un casi-semiring , y algunas mónadas aditivas califican como tales. Sin embargo, no todas las mónadas aditivas cumplen las leyes distributivas incluso de un casi-semiring. [ 32 ]
  19. En Haskell, extend se define en realidad con las entradas intercambiadas, pero como no se utiliza currificación en este artículo, se define aquí como el dual exacto de bind .

Referencias

  1. 1 2 3 4 5 6 O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). "Mónadas" . Haskell del mundo real . Sebastopol, California: O'Reilly Media. Capítulo 14. ISBN 978-0596514983.
  2. 1 2 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. 1 2 3 Moggi, Eugenio (1991). "Nociones de computación y mónadas" (PDF) . Information and Computation . 93 (1): 55– 92. CiteSeerX 10.1.1.158.5275 . doi : 10.1016/0890-5401(91)90052-4 . 
  4. 1 2 3 4 5 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. 1 2 Hudak, Paul ; Peterson, John; Fasel, Joseph (1999). "Acerca de las mónadas" . Una introducción sencilla a Haskell 98. Capítulo 9.
  6. 1 2 Respuesta de C. A. McCann (23 de julio de 2010 a las 23:39) ¿Cómo y por qué funciona la mónada Haskell Cont?
  7. 1 2 3 Graham Hutton (2016) Programación en Haskell 2.ª edición
  8. 1 2 Beckman, 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 theoretical foundation for AOP (PDF) . International Workshop on Aspect Oriented Programming at 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). "Using 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 del original el 20 de febrero de 2018. Recuperado 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. Recuperado el 19 de noviembre de 2018 .
  17. Kiselyov, Olag (2007). "Continuaciones delimitadas en sistemas operativos". Modelado y uso del contexto . Notas de clase en informática. Vol. 4635. Springer Berlin Heidelberg. pp. 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). "Tu 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 está inducida por un par de functores adjuntos" (PDF) . Actas de la Sociedad Matemática Americana . 16 (3): 544– 546. doi : 10.1090/S0002-9939-1965-0177024-4 . Consultado el 19 de noviembre de 2018 .
  21. ^ Pimienta, Peter, 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 Ciencias de la Computación. Pacific Grove, California. CiteSeerX 10.1.1.26.2787 . 
  23. 1 2 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 Typeclassopedia
  25. Stack overflow (8 de septiembre de 2017) Definir 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 del original el 30 de octubre de 2018. Recuperado el 20 de noviembre de 2018 .
  28. 1 2 Gibbard, Cale (30 de diciembre de 2011). "Mónadas como contenedores" . HaskellWiki . Haskell.org. Archivado del original el 14 de diciembre de 2017. Recuperado el 20 de noviembre de 2018 .
  29. 1 2 Piponi, Dan (7 de agosto de 2006). "¡Podrías haber inventado las mónadas! (Y tal vez ya lo hayas hecho)" . Un vecindario del infinito . Archivado del original el 24 de octubre de 2018. Recuperado el 16 de octubre de 2018 .
  30. "Algunos detalles sobre las expresiones de cálculo en 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 del original el 29 de enero de 2018. Recuperado el 25 de noviembre de 2018 .
  32. 1 2 Rivas, Exequiel; Jaskelioff, Mauro; Schrijvers, Tom (julio de 2015). De monoides a casi semianillos: la esencia de MonadPlus y Alternative (PDF) . 17º Simposio Internacional ACM sobre Principios y Práctica de la 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 . ISSN 1469-7653 . S2CID 21038598 .   
  34. Kiselyov, Oleg (mayo de 2012). Schrijvers, Tom; Thiemann, Peter (eds.). Iterados (PDF) . Simposio Internacional sobre Programación Funcional y Lógica. Lecture Notes in Computer Science. Vol. 7294. Kobe, Japón: 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 (julio de 2005). Horváth, Zoltán (ed.). La esencia de la programación de flujo de datos (PDF) . Primera Escuela de Verano, Programación Funcional Centroeuropea. Lecture Notes in Computer Science. 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" . 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 (mayo de 2002). "Combinando una mónada y una comónada" (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 la calificación (PDF) . 21.ª Conferencia Internacional 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.

Referencias de HaskellWiki:

  • " Todo sobre las mónadas " (originalmente de Jeff Newbern): un análisis exhaustivo de todas las mónadas comunes y cómo funcionan en Haskell; incluye la analogía de la "cadena de montaje mecanizada".
  • " Typeclassopedia " (originalmente de Brent Yorgey): una exposición detallada de cómo se interrelacionan las principales clases de tipos en Haskell, incluidas las mónadas.

Tutoriales:

  • " Un puñado de mónadas " (del libro de texto en línea de Haskell ¡ Aprende Haskell para un gran bien! — Un capítulo que introduce las mónadas desde el punto de partida de las clases de tipos de functores y functores aplicativos, incluyendo ejemplos.
  • " Para algunas mónadas más " — Un segundo capítulo que explica más detalles y ejemplos, incluyendo una Probabilitymónada para cadenas de Markov .
  • " Functores, Aplicativos y Mónadas en Imágenes (por Aditya Bhargava) — Un tutorial rápido, humorístico y visual sobre mónadas."

Casos interesantes:

  • " Las tuberías de UNIX como mónadas de E/S " (por Oleg Kiselyov) — Un breve ensayo que explica cómo las tuberías de Unix son efectivamente monádicas.
  • Pro Scala: Patrones de diseño monádicos para la web (por Gregory Meredith) — Un manuscrito inédito y completo sobre cómo mejorar muchos aspectos del desarrollo web en Scala con mónadas.