haskell monada - ¿Qué es una mónada?




en maybe (25)

En realidad, contrariamente a la comprensión común de las mónadas, no tienen nada que ver con el estado. Las mónadas son simplemente una forma de envolver cosas y proporcionar métodos para realizar operaciones en las cosas envueltas sin desenvolverlas.

Por ejemplo, puede crear un tipo para envolver otro, en Haskell:

data Wrapped a = Wrap a

Para envolver cosas que definimos

return :: a -> Wrapped a
return x = Wrap x

Para realizar operaciones sin desenvolver, digamos que tiene una función f :: a -> b , luego puede hacer esto para levantar esa función para actuar sobre los valores envueltos:

fmap :: (a -> b) -> (Wrapped a -> Wrapped b)
fmap f (Wrap x) = Wrap (f x)

Eso es todo lo que hay que entender. Sin embargo, resulta que hay una función más general para hacer este levantamiento , que es bind :

bind :: (a -> Wrapped b) -> (Wrapped a -> Wrapped b)
bind f (Wrap x) = f x

bind puede hacer un poco más que fmap , pero no al revés. En realidad, fmap se puede definir en términos de bind y return . Entonces, cuando se define una mónada ... da su tipo (aquí estaba Wrapped a ) y luego dice cómo funcionan sus operaciones de return y bind .

Lo bueno es que esto resulta ser un patrón tan general que aparece por todas partes, encapsular el estado de manera pura es solo uno de ellos.

Para un buen artículo sobre cómo se pueden usar las mónadas para introducir dependencias funcionales y así controlar el orden de evaluación, como se usa en la mónada IO de Haskell, consulte IO Inside .

En cuanto a la comprensión de las mónadas, no te preocupes demasiado por eso. Lee sobre ellos lo que te parezca interesante y no te preocupes si no entiendes de inmediato. Entonces solo bucear en un idioma como Haskell es el camino a seguir. Las mónadas son una de estas cosas donde la comprensión se filtra en tu cerebro con la práctica; un día, de repente, te das cuenta de que las comprendes.

Habiendo examinado brevemente a Haskell recientemente, ¿cuál sería una explicación breve, breve y práctica sobre qué es esencialmente una mónada?

He encontrado que la mayoría de las explicaciones que he encontrado son bastante inaccesibles y carecen de detalles prácticos.


[Descargo de responsabilidad: todavía estoy tratando de asimilar completamente las mónadas. Lo siguiente es justo lo que he entendido hasta ahora. Si está mal, espero que alguien con conocimientos me llame a la alfombra.]

Arnar escribió:

Las mónadas son simplemente una forma de envolver cosas y proporcionar métodos para realizar operaciones en las cosas envueltas sin desenvolverlas.

Eso es precisamente eso. La idea es la siguiente:

  1. Toma algún tipo de valor y envuélvalo con información adicional. Al igual que el valor es de un cierto tipo (por ejemplo, un entero o una cadena), la información adicional es de un cierto tipo.

    Por ejemplo, esa información adicional podría ser un Maybe o un IO .

  2. Luego tiene algunos operadores que le permiten operar con los datos envueltos mientras transporta esa información adicional. Estos operadores utilizan la información adicional para decidir cómo cambiar el comportamiento de la operación en el valor ajustado.

    Por ejemplo, un Maybe Int puede ser un Just Int o Nothing . Ahora, si agrega un Maybe Int a Maybe Int , el operador verificará si ambos están dentro de Just Int , y si es así, desenvolverá los Int , los pasará al operador de suma, volverá a envolver el Int resultante. en un nuevo Just Int (que es un Maybe Int válido) y, por lo tanto, devolver un Maybe Int . Pero si uno de ellos era un Nothing interior, este operador simplemente devolverá Nothing , que nuevamente es un Maybe Int válido. De esa manera, puedes fingir que tus Maybe Int s son solo números normales y realizar cálculos regulares en ellos. Si obtuvieras una Nothing , tus ecuaciones seguirán produciendo el resultado correcto, sin que tengas que tirar basura por Nothing todas partes .

Pero el ejemplo es justo lo que sucede para Maybe . Si la información adicional fuera una IO , entonces se llamaría a ese operador especial definido para las IO , y podría hacer algo totalmente diferente antes de realizar la adición. (De acuerdo, agregar dos IO Int s juntos probablemente no tenga sentido, aún no estoy seguro). (Además, si prestó atención al ejemplo de Maybe , notó que "envolver un valor con cosas adicionales" no siempre es correcto. Pero es difícil ser exacto, correcto y preciso sin ser inescrutable.)

Básicamente, "mónada" significa aproximadamente "patrón" . Pero en lugar de un libro lleno de patrones explicados de manera informal y con nombres específicos, ahora tiene una construcción de lenguaje (sintaxis y todo) que le permite declarar nuevos patrones como elementos de su programa . (La imprecisión aquí es que todos los patrones tienen que seguir una forma particular, por lo que una mónada no es tan genérica como un patrón. Pero creo que ese es el término más cercano que la mayoría de la gente conoce y entiende).

Y es por eso que la gente encuentra las mónadas tan confusas: porque son un concepto tan genérico. Preguntar qué hace que una mónada sea algo es igualmente vago que preguntar qué hace que algo sea un patrón.

Pero piense en las implicaciones de tener soporte sintáctico en el lenguaje para la idea de un patrón: en lugar de tener que leer el libro Gang of Four y memorizar la construcción de un patrón particular, simplemente escriba un código que implemente este patrón en un agnóstico. Manera genérica una vez y luego ya está! Luego puede reutilizar este patrón, como Visitante o Estrategia o Fachada o lo que sea, simplemente decorando las operaciones en su código con él, ¡sin tener que volver a implementarlo una y otra vez!

Entonces, es por eso que las personas que entienden las mónadas las encuentran tan útiles : no es un concepto de torre de marfil que los intelectuales intelectuales se enorgullecen de entender (bueno, eso también, por supuesto, teehee), pero en realidad simplifican el código.


He estado pensando en las Mónadas de una manera diferente, últimamente. He estado pensando en ellos como abstraer el orden de ejecución de una manera matemática, lo que hace posible nuevos tipos de polimorfismo.

Si está utilizando un lenguaje imperativo y escribe algunas expresiones en orden, el código SIEMPRE se ejecuta exactamente en ese orden.

Y en el caso simple, cuando usa una mónada, se siente igual: define una lista de expresiones que suceden en orden. Excepto que, dependiendo de la mónada que use, su código podría ejecutarse en orden (como en la mónada IO), en paralelo sobre varios elementos a la vez (como en la mónada Lista), podría detenerse a la mitad (como en la mónada Quizás) , puede hacer una pausa a mitad de camino para reanudarse más tarde (como en una mónada de reanudación), puede retroceder y comenzar desde el principio (como en una mónada de transacción), o puede retroceder parcialmente para probar otras opciones (como en una mónada lógica) .

Y como las mónadas son polimórficas, es posible ejecutar el mismo código en diferentes mónadas, según sus necesidades.

Además, en algunos casos, es posible combinar mónadas juntas (con transformadores de mónada) para obtener múltiples funciones al mismo tiempo.



En el contexto de Scala, encontrarás lo siguiente como la definición más simple. Básicamente, flatMap (o bind) es 'asociativo' y existe una identidad.

trait M[+A] {
  def flatMap[B](f: A => M[B]): M[B] // AKA bind

  // Pseudo Meta Code
  def isValidMonad: Boolean = {
    // for every parameter the following holds
    def isAssociativeOn[X, Y, Z](x: M[X], f: X => M[Y], g: Y => M[Z]): Boolean =
      x.flatMap(f).flatMap(g) == x.flatMap(f(_).flatMap(g))

    // for every parameter X and x, there exists an id
    // such that the following holds
    def isAnIdentity[X](x: M[X], id: X => M[X]): Boolean =
      x.flatMap(id) == x
  }
}

P.ej

// These could be any functions
val f: Int => Option[String] = number => if (number == 7) Some("hello") else None
val g: String => Option[Double] = string => Some(3.14)

// Observe these are identical. Since Option is a Monad 
// they will always be identical no matter what the functions are
scala> Some(7).flatMap(f).flatMap(g)
res211: Option[Double] = Some(3.14)

scala> Some(7).flatMap(f(_).flatMap(g))
res212: Option[Double] = Some(3.14)


// As Option is a Monad, there exists an identity:
val id: Int => Option[Int] = x => Some(x)

// Observe these are identical
scala> Some(7).flatMap(id)
res213: Option[Int] = Some(7)

scala> Some(7)
res214: Some[Int] = Some(7)

NOTA Estrictamente hablando, la definición de una mónada en la programación funcional no es la definición de una mónada en la teoría de categorías , que se define en los turnos de mapy flatten. Aunque son una especie de equivalente en ciertas asignaciones. Esta presentación es muy buena: http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-category


Primero: el término mónada es un poco vacío si no eres matemático. Un término alternativo es constructor de computación, que es un poco más descriptivo de lo que realmente son útiles para.

Pides ejemplos prácticos:

Ejemplo 1: Lista de comprensión :

[x*2 | x<-[1..10], odd x]

Esta expresión devuelve los dobles de todos los números impares en el rango de 1 a 10. ¡Muy útil!

Resulta que esto es realmente solo azúcar sintáctica para algunas operaciones dentro de la mónada de la Lista. La misma lista de comprensión se puede escribir como:

do
   x <- [1..10]
   if odd x 
       then [x * 2] 
       else []

O incluso:

[1..10] >>= (\x -> if odd x then [x*2] else [])

Ejemplo 2: Entrada / Salida :

do
   putStrLn "What is your name?"
   name <- getLine
   putStrLn ("Welcome, " ++ name ++ "!")

Ambos ejemplos utilizan mónadas, constructores de cómputo AKA. El tema común es que la mónada encadena las operaciones de una manera específica y útil. En la lista de comprensión, las operaciones se encadenan de manera que si una operación devuelve una lista, se realizan las siguientes operaciones en cada elemento de la lista. La mónada IO, por otro lado, realiza las operaciones de forma secuencial, pero pasa una "variable oculta", que representa "el estado del mundo", que nos permite escribir código de E / S de una manera totalmente funcional.

Resulta que el patrón de las operaciones de encadenamiento es bastante útil y se utiliza para muchas cosas diferentes en Haskell.

Otro ejemplo son las excepciones: al utilizar la mónada Error , las operaciones se encadenan de manera que se realizan de forma secuencial, excepto si se produce un error, en cuyo caso se abandona el resto de la cadena.

Tanto la sintaxis de lista-comprensión como la notación-do son azúcar sintáctico para operaciones de encadenamiento usando el operador >>= . Una mónada es básicamente un tipo que admite el operador >>= .

Ejemplo 3: un analizador

Este es un analizador muy simple que analiza una cadena entre comillas o un número:

parseExpr = parseString <|> parseNumber

parseString = do
        char '"'
        x <- many (noneOf "\"")
        char '"'
        return (StringValue x)

parseNumber = do
    num <- many1 digit
    return (NumberValue (read num))

Las operaciones char , digit , etc. son bastante simples. Ellos coinciden o no coinciden. La magia es la mónada que gestiona el flujo de control: las operaciones se realizan secuencialmente hasta que falla una coincidencia, en cuyo caso la mónada retrocede hasta la última <|> e intenta la siguiente opción. De nuevo, una forma de encadenar las operaciones con algunas semánticas útiles adicionales.

Ejemplo 4: Programación asíncrona.

Los ejemplos anteriores están en Haskell, pero resulta que F# también es compatible con mónadas. Este ejemplo es robado de Don Syme :

let AsyncHttp(url:string) =
    async {  let req = WebRequest.Create(url)
             let! rsp = req.GetResponseAsync()
             use stream = rsp.GetResponseStream()
             use reader = new System.IO.StreamReader(stream)
             return reader.ReadToEnd() }

Este método recupera una página web. La línea de perforación es el uso de GetResponseAsync : en realidad espera la respuesta en un subproceso independiente, mientras que el subproceso principal regresa de la función. Las últimas tres líneas se ejecutan en el subproceso generado cuando se ha recibido la respuesta.

En la mayoría de los otros idiomas, tendría que crear explícitamente una función separada para las líneas que manejan la respuesta. La mónada async puede "dividir" el bloque por sí sola y posponer la ejecución de la segunda mitad. (La sintaxis async {} indica que el flujo de control en el bloque está definido por la mónada async ).

Cómo trabajan ellos

Entonces, ¿cómo puede una mónada hacer todo esto de manera sofisticada de control-flujo? Lo que realmente sucede en un do-block (o una expresión de cómputo como se llama en F #), es que cada operación (básicamente cada línea) está envuelta en una función anónima separada. Estas funciones se combinan luego utilizando el operador de vinculación (escrito >>= en Haskell). Dado que la operación de bind combina funciones, puede ejecutarlas como le parezca: secuencialmente, varias veces, a la inversa, descartar algunas, ejecutar algunas en un hilo separado cuando lo desee y así sucesivamente.

Como ejemplo, esta es la versión expandida del código IO del ejemplo 2:

putStrLn "What is your name?"
>>= (\_ -> getLine)
>>= (\name -> putStrLn ("Welcome, " ++ name ++ "!"))

Esto es más feo, pero también es más obvio lo que realmente está sucediendo. El operador >>= es el ingrediente mágico: toma un valor (en el lado izquierdo) y lo combina con una función (en el lado derecho), para producir un nuevo valor. Este nuevo valor es tomado por el siguiente operador >>= y nuevamente se combina con una función para producir un nuevo valor. >>= se puede ver como un mini-evaluador.

Tenga en cuenta que >>= está sobrecargado para diferentes tipos, por lo que cada mónada tiene su propia implementación de >>= . (Todas las operaciones en la cadena deben ser del tipo de la misma mónada, de lo contrario, el operador >>= no funcionará).

La implementación más simple posible de >>= solo toma el valor de la izquierda y lo aplica a la función de la derecha y devuelve el resultado, pero como se dijo antes, lo que hace que todo el patrón sea útil es cuando hay algo adicional en el Implementación de la mónada de >>= .

Existe cierta inteligencia adicional sobre cómo se pasan los valores de una operación a la siguiente, pero esto requiere una explicación más profunda del sistema de tipo Haskell.

Resumiendo

En términos de Haskell, una mónada es un tipo parametrizado que es una instancia de la clase de tipo Mónada, que define >>= junto con algunos otros operadores. En términos sencillos, una mónada es solo un tipo para el que se define la operación >>= .

En sí mismo >>= es solo una forma incómoda de encadenar funciones, pero con la presencia de la do-notación que oculta la "plomería", las operaciones monádicas resultan ser una abstracción muy agradable y útil, muchos lugares útiles en el lenguaje. , y útil para crear tus propios mini idiomas en el idioma.

¿Por qué son difíciles las mónadas?

Para muchos estudiantes de Haskell, las mónadas son un obstáculo que golpean como un muro de ladrillos. No es que las mónadas sean complejas, sino que la implementación se basa en muchas otras características avanzadas de Haskell, como los tipos parametrizados, las clases de tipos, etc. El problema es que Haskell I / O se basa en mónadas, y I / O es probablemente una de las primeras cosas que quiere entender al aprender un nuevo idioma. Después de todo, no es muy divertido crear programas que no produzcan ningún idioma. salida. No tengo una solución inmediata para este problema del huevo y la gallina, excepto que tratar la E / S como "la magia sucede aquí" hasta que tenga suficiente experiencia con otras partes del lenguaje. Lo siento.

Excelente blog sobre mónadas: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html


Todavía soy nuevo en las mónadas, pero pensé que iba a compartir un enlace que encontré que me sentí muy bien de leer (¡¡CON IMÁGENES !!): http://www.matusiak.eu/numerodix/blog/2012/3/11/monads-for-the-layman/ (sin afiliación)

Básicamente, el concepto cálido y difuso que obtuve del artículo fue el concepto de que las mónadas son básicamente adaptadores que permiten que las funciones dispares funcionen de manera compositiva, es decir, poder encadenar múltiples funciones y mezclarlas y combinarlas sin preocuparse por un retorno inconsistente tipos y tal. Así que la función BIND se encarga de mantener las manzanas con manzanas y las naranjas con naranjas cuando intentamos hacer estos adaptadores. Y la función LIFT se encarga de tomar las funciones de "nivel inferior" y de "actualizarlas" para que funcionen con las funciones BIND y que también se puedan componer.

Espero haberlo hecho bien y, lo que es más importante, espero que el artículo tenga una visión válida de las mónadas. Si nada más, este artículo me ayudó a abrir mi apetito por aprender más sobre las mónadas.


Mi tutorial favorito de mónada:

http://www.haskell.org/haskellwiki/All_About_Monads

(De un total de 170,000 resultados en una búsqueda de Google para "tutorial de mónada"!)

@Stu: el punto de las mónadas es permitirle agregar (normalmente) semántica secuencial a un código puro; incluso puede componer mónadas (utilizando Monad Transformers) y obtener semánticas combinadas más interesantes y complicadas, como análisis con manejo de errores, estado compartido y registro, por ejemplo. Todo esto es posible en código puro, las mónadas solo le permiten abstraerlo y reutilizarlo en bibliotecas modulares (siempre es bueno en programación), además de proporcionar una sintaxis conveniente para que se vea imperativo.

Haskell ya tiene una sobrecarga de operadores [1]: usa clases de tipos de la misma forma en que uno podría usar interfaces en Java o C #, pero también resulta que Haskell también permite tokens no alfanuméricos como + && y> como identificadores de infijo. Es solo la sobrecarga del operador en su forma de verlo si quiere decir "sobrecargar el punto y coma" [2]. Suena como magia negra y pidiendo problemas para "sobrecargar el punto y coma" (imagen de los piratas informáticos de Perl que se están dando cuenta de esta idea) pero el punto es que sin mónadas no hay punto y coma, ya que el código puramente funcional no requiere ni permite la secuenciación explícita.

Todo esto suena mucho más complicado de lo que necesita. El artículo de sigfpe es bastante bueno, pero utiliza Haskell para explicarlo, lo que no logra romper el problema de la gallina y el huevo de entender a Haskell para tratar a las Mónadas y entender a las Mónadas para hacerles daño a Haskell.

[1] Este es un problema separado de las mónadas pero las mónadas utilizan la función de sobrecarga del operador de Haskell.

[2] Esto también es una simplificación excesiva ya que el operador para encadenar acciones monádicas es >> = (pronunciado "bind") pero hay un azúcar sintáctico ("do") que le permite usar llaves y puntos y / o sangría y líneas nuevas.



Trataré de explicar Monaden el contexto de Haskell.

En la programación funcional, la composición de la función es importante. Permite que nuestro programa conste de funciones pequeñas y fáciles de leer.

Digamos que tenemos dos funciones: g :: Int -> Stringy f :: String -> Bool.

Podemos hacer (f . g) x, que es lo mismo que f (gx), donde xes un Intvalor.

Al hacer la composición / aplicar el resultado de una función a otra, es importante que los tipos coincidan. En el caso anterior, el tipo de resultado devuelto gdebe ser el mismo que el tipo aceptado por f.

Pero a veces los valores están en contextos, y esto hace que sea un poco menos fácil alinear los tipos. (Tener valores en contextos es muy útil. Por ejemplo, el Maybe Inttipo representa un Intvalor que puede no estar allí, el IO Stringtipo representa un Stringvalor que está ahí como resultado de la realización de algunos efectos secundarios).

Digamos que ahora tenemos g1 :: Int -> Maybe Stringy f1 :: String -> Maybe Bool. g1y f1son muy similares gy frespectivamente.

No podemos hacer (f1 . g1) xo f1 (g1 x), donde xes un Intvalor. El tipo de resultado devuelto por g1no es lo que se f1espera.

Podríamos componer fy gcon el .operador, pero ahora no podemos componer f1y g1con. . El problema es que no podemos pasar directamente un valor en un contexto a una función que espera un valor que no está en un contexto.

¿No sería bueno que introdujéramos un operador para componer g1y f1, de manera que podamos escribir (f1 OPERATOR g1) x? g1devuelve un valor en un contexto. El valor se sacará de contexto y se aplicará a f1. Y sí, tenemos tal operador. Es <=<.

También tenemos el >>=operador que hace exactamente lo mismo para nosotros, aunque en una sintaxis ligeramente diferente.

Escribimos: g1 x >>= f1. g1 xes un Maybe Intvalor El >>=operador ayuda a sacar ese Intvalor del contexto "quizás no está ahí", y aplicarlo f1. El resultado de f1, que es un Maybe Bool, será el resultado de toda la >>=operación.

Y finalmente, ¿por qué es Monadútil? Porque Monades la clase de tipo que define al >>=operador, muy parecida a la Eqclase de tipo que define a los operadores ==y /=.

Para concluir, la Monadclase de tipo define el >>=operador que nos permite pasar valores en un contexto (llamamos a estos valores monádicos) a funciones que no esperan valores en un contexto. El contexto será atendido.

Si hay algo que recordar aquí, es que Monadpermite la composición de funciones que involucra valores en contextos .


Una mónada es una forma de combinar cálculos que comparten un contexto común. Es como construir una red de tuberías. Al construir la red, no hay datos que fluyan a través de ella. Pero cuando he terminado de unir todos los bits con 'bind' y 'return', invoco algo así runMyMonad monad datay los datos fluyen a través de las tuberías.


En la práctica, la mónada es una implementación personalizada del operador de composición de funciones que se ocupa de los efectos secundarios y los valores de entrada y retorno incompatibles (para el encadenamiento).


Primero debes entender qué es un funtor. Antes de eso, entender las funciones de orden superior.

Una función de orden superior es simplemente una función que toma una función como un argumento.

Un functor es cualquier tipo de construcción T para la cual existe una función de orden superior, llamada map , que transforma una función de tipo a -> b (dado cualquiera de los dos tipos a y b ) en una función T a -> T b . Esta función de map también debe obedecer las leyes de identidad y composición de modo que las siguientes expresiones devuelvan verdadero para todos p y q (notación de Haskell):

map id = id
map (p . q) = map p . map q

Por ejemplo, un constructor de tipo llamado List es un functor si viene equipado con una función de tipo (a -> b) -> List a -> List b que cumple con las leyes anteriores. La única implementación práctica es obvia. La función List a -> List b resultante se repite sobre la lista dada, llama a la función (a -> b) para cada elemento y devuelve la lista de los resultados.

Una mónada es esencialmente un funtor T con dos métodos adicionales, join , de tipo T (T a) -> T a , y una unit (a veces llamada return , fork o pure ) de tipo a -> T a . Para listas en Haskell:

join :: [[a]] -> [a]
pure :: a -> [a]

¿Por qué es útil? Porque podría, por ejemplo, map sobre una lista con una función que devuelve una lista. Join toma la lista resultante de listas y las concatena. List es una mónada porque esto es posible.

Puedes escribir una función que haga un map , luego join . Esta función se llama bind , o flatMap , o (>>=) , o (=<<) . Esto es normalmente cómo se da una instancia de mónada en Haskell.

Una mónada tiene que satisfacer ciertas leyes, a saber, que la join debe ser asociativa. Esto significa que si tiene un valor x de tipo [[[a]]] entonces join (join x) debe ser igual a join (map join x) . Y pure debe ser una identidad para join tal manera que join (pure x) == x .


PrincessLa explicación de Princess sobre F # Computation Expressions me ayudó, aunque todavía no puedo decir que realmente lo entendí.

EDITAR : esta serie, que explica las mónadas con javascript, es la que "inclinó la balanza" para mí.

Creo que entender las mónadas es algo que te arrastra. En ese sentido, leer tantos 'tutoriales' como puedas es una buena idea, pero a menudo las cosas extrañas (lenguaje o sintaxis desconocidas) evitan que tu cerebro se concentre en lo esencial.

Algunas cosas que tuve dificultad para entender:

  • Las explicaciones basadas en reglas nunca me funcionaron, porque la mayoría de los ejemplos prácticos en realidad requieren más que solo devolver / enlazar.
  • Además, llamarlas reglas no ayudó. Es más un caso de "hay estas cosas que tienen algo en común, llamémoslas 'mónadas' y los bits en 'reglas' comunes".
  • Volver ( a -> M<a>) y bind ( M<a> -> (a -> M<b>) -> M<b>) son grandes, pero lo que nunca podría entender es cómo enlazar podría extraer el ade M<a>fin de pasarlo a a -> M<b>. No creo que haya leído en ninguna parte (tal vez sea obvio para todos los demás), que el reverso de Retorno ( M<a> -> a) tiene que existir dentro de la mónada, simplemente no necesita estar expuesto.


¡Pero, podrías haber inventado las monadas!

sigfpe dice:

Pero todas estas introducen las mónadas como algo esotérico que necesita una explicación. Pero lo que quiero argumentar es que no son en absoluto esotéricos. De hecho, ante diversos problemas en la programación funcional, habrían sido conducidos, inexorablemente, a ciertas soluciones, todas las cuales son ejemplos de mónadas. De hecho, espero conseguir que los inventes ahora si aún no lo has hecho. Entonces es un pequeño paso notar que todas estas soluciones son, de hecho, la misma solución disfrazada. Y después de leer esto, podría estar en una mejor posición para entender otros documentos en las mónadas porque reconocerá todo lo que ve como algo que ya ha inventado.

Muchos de los problemas que las mónadas intentan resolver están relacionados con el problema de los efectos secundarios. Así que vamos a empezar con ellos. (Tenga en cuenta que las mónadas le permiten hacer más que controlar los efectos secundarios, en particular, muchos tipos de objetos contenedores pueden verse como mónadas. Algunas de las introducciones a las mónadas tienen dificultades para conciliar estos dos usos diferentes de las mónadas y concentrarse en una sola o el otro.)

En un lenguaje de programación imperativo como C ++, las funciones no se comportan como las funciones de las matemáticas. Por ejemplo, supongamos que tenemos una función de C ++ que toma un solo argumento de punto flotante y devuelve un resultado de punto flotante. Superficialmente, puede parecer un poco como una función matemática que relaciona los reales con los reales, pero una función de C ++ puede hacer algo más que devolver un número que depende de sus argumentos. Puede leer y escribir los valores de las variables globales, así como escribir la salida en la pantalla y recibir información del usuario. Sin embargo, en un lenguaje funcional puro, una función solo puede leer lo que se le proporciona en sus argumentos y la única forma en que puede tener un efecto en el mundo es a través de los valores que devuelve.


Después de mucho esfuerzo, creo que finalmente entiendo la mónada. Después de releer mi propia crítica larga de la abrumadoramente votada respuesta, ofreceré esta explicación.

Hay tres preguntas que deben ser respondidas para entender las mónadas:

  1. ¿Por qué necesitas una mónada?
  2. ¿Qué es una mónada?
  3. ¿Cómo se implementa una mónada?

Como señalé en mis comentarios originales, demasiadas explicaciones de la mónada quedan atrapadas en la pregunta número 3, sin, y antes de cubrir adecuadamente la pregunta 2, o la pregunta 1.

¿Por qué necesitas una mónada?

Los lenguajes funcionales puros como Haskell son diferentes de los lenguajes imperativos como C, o Java en eso, un programa funcional puro no necesariamente se ejecuta en un orden específico, paso a paso. Un programa de Haskell es más parecido a una función matemática, en la que puedes resolver la "ecuación" en cualquier número de órdenes potenciales. Esto confiere una serie de beneficios, entre los que se encuentra que elimina la posibilidad de ciertos tipos de errores, en particular los relacionados con cosas como "estado".

Sin embargo, hay ciertos problemas que no son tan sencillos de resolver con este estilo de programación. Algunas cosas, como la programación de la consola y la entrada / salida del archivo, necesitan que suceda en un orden particular, o necesitan mantenerse en estado. Una forma de resolver este problema es crear un tipo de objeto que represente el estado de un cálculo y una serie de funciones que tomen un objeto de estado como entrada y devuelvan un nuevo objeto de estado modificado.

Así que vamos a crear un valor hipotético de "estado", que representa el estado de una pantalla de consola. exactamente cómo se construye este valor no es importante, pero digamos que es una matriz de caracteres ASCII de longitud de byte que representa lo que se ve actualmente en la pantalla, y una matriz que representa la última línea de entrada ingresada por el usuario, en pseudocódigo. Hemos definido algunas funciones que toman el estado de la consola, la modifican y devuelven un nuevo estado de consola.

consolestate MyConsole = new consolestate;

Así que para hacer la programación de la consola, pero de una manera puramente funcional, necesitaría anidar una gran cantidad de llamadas de funciones entre sí.

consolestate FinalConsole = print(input(print(myconsole, "Hello, what's your name?")),"hello, %inputbuffer%!");

La programación de esta manera mantiene el estilo funcional "puro", mientras obliga a que los cambios en la consola ocurran en un orden particular. Pero, probablemente queramos hacer más que unas pocas operaciones a la vez, como en el ejemplo anterior. Las funciones de anidación de esa manera comenzarán a volverse desgarbadas. Lo que queremos, es un código que haga esencialmente lo mismo que arriba, pero se escriba un poco más como esto:

consolestate FinalConsole = myconsole:
                            print("Hello, what's your name?"):
                            input():
                            print("hello, %inputbuffer%!");

De hecho, esta sería una forma más conveniente de escribirlo. ¿Cómo hacemos eso sin embargo?

¿Qué es una mónada?

Una vez que tenga un tipo (como consolestate) que defina junto con un grupo de funciones diseñadas específicamente para operar en ese tipo, puede convertir todo el paquete de estas cosas en una "mónada" definiendo un operador como :(enlace) que automáticamente los valores de retorno a su izquierda, los parámetros de función a su derecha y un liftoperador que convierte las funciones normales, en funciones que funcionan con ese tipo específico de operador de enlace.

¿Cómo se implementa una mónada?

Ver otras respuestas, que parecen bastante libres para saltar a los detalles de eso.


Una mónada es, efectivamente, una forma de "operador de tipo". Hará tres cosas. Primero, "envolverá" (o convertirá) un valor de un tipo en otro tipo (típicamente llamado "tipo monádico"). En segundo lugar, hará que todas las operaciones (o funciones) estén disponibles en el tipo subyacente disponible en el tipo monádico. Finalmente, proporcionará soporte para combinarse con otra mónada para producir una mónada compuesta.

La "quizás mónada" es esencialmente el equivalente de "tipos anulables" en Visual Basic / C #. Toma un tipo no anulable "T" y lo convierte en un "Nullable <T>", y luego define lo que significan todos los operadores binarios en un Nullable <T>.

Los efectos secundarios se representan de forma similar. Se crea una estructura que contiene descripciones de efectos secundarios junto con el valor de retorno de una función. Las operaciones "elevadas" luego se copian alrededor de los efectos secundarios a medida que se pasan valores entre las funciones.

Se les llama "mónadas" en lugar del nombre más fácil de entender de "operadores de tipo" por varias razones:

  1. Las mónadas tienen restricciones sobre lo que pueden hacer (ver la definición para más detalles).
  2. Esas restricciones, junto con el hecho de que hay tres operaciones involucradas, se ajustan a la estructura de algo llamado mónada en la teoría de categorías, que es una rama oscura de las matemáticas.
  3. Fueron diseñados por los proponentes de lenguajes funcionales "puros".
  4. Proponentes de lenguajes puramente funcionales como ramas oscuras de las matemáticas.
  5. Debido a que las matemáticas son oscuras, y las mónadas están asociadas con estilos particulares de programación, las personas tienden a usar la palabra mónada como una especie de saludo secreto. Debido a esto nadie se ha molestado en invertir en un nombre mejor.

Las mónadas son para controlar el flujo de lo que los tipos de datos abstractos son para los datos.

En otras palabras, muchos desarrolladores se sienten cómodos con la idea de Conjuntos, Listas, Diccionarios (o Hashes o Mapas) y Árboles. Dentro de esos tipos de datos hay muchos casos especiales (por ejemplo, InsertionOrderPreservingIdentityHashMap).

Sin embargo, cuando se enfrentan con el "flujo" del programa, muchos desarrolladores no han sido expuestos a muchas más construcciones que si, switch / case, do, while, goto (grr), y (quizás) cierres.

Entonces, una mónada es simplemente una construcción de flujo de control. Una mejor frase para reemplazar la mónada sería "tipo de control".

Como tal, una mónada tiene ranuras para la lógica de control, o declaraciones, o funciones; el equivalente en estructuras de datos sería decir que algunas estructuras de datos le permiten agregar datos y eliminarlos.

Por ejemplo, la mónada "if":

if( clause ) then block

en su forma más simple tiene dos espacios: una cláusula y un bloque. La ifmónada generalmente se construye para evaluar el resultado de la cláusula, y si no es falso, evalúa el bloque. Muchos desarrolladores no son introducidos a las mónadas cuando aprenden 'si', y simplemente no es necesario entender las mónadas para escribir una lógica efectiva.

Las mónadas pueden complicarse, de la misma manera que las estructuras de datos pueden complicarse, pero hay muchas categorías amplias de mónadas que pueden tener una semántica similar, pero diferentes implementaciones y sintaxis.

Por supuesto, de la misma manera que las estructuras de datos pueden ser iteradas o recorridas, se pueden evaluar las mónadas.

Los compiladores pueden o no tener soporte para mónadas definidas por el usuario. Haskell ciertamente lo hace. Ioke tiene algunas capacidades similares, aunque el término mónada no se usa en el idioma.


Además de las excelentes respuestas anteriores, permítame ofrecerle un enlace al siguiente artículo (por Patrick Thomson) que explica las mónadas al relacionar el concepto con la biblioteca de JavaScript jQuery (y su forma de usar el "encadenamiento de métodos" para manipular el DOM) : jQuery es una mónada

La documentación de jQuery en sí no se refiere al término "mónada", sino que habla del "patrón de construcción", que probablemente sea más familiar. Esto no cambia el hecho de que tienes una mónada adecuada allí, tal vez sin siquiera darse cuenta.


Lo que el mundo necesita es otra publicación de blog de mónadas, pero creo que esto es útil para identificar las mónadas existentes en la naturaleza.

Lo anterior es un fractal llamado triángulo de Sierpinski, el único fractal que puedo recordar dibujar. Los fractales son una estructura similar a la del triángulo anterior, en la que las partes son similares a la totalidad (en este caso, exactamente la mitad de la escala como triángulo principal)

Las mónadas son fractales. Dada una estructura de datos monádica, sus valores se pueden componer para formar otro valor de la estructura de datos. Por eso es útil para la programación, y por eso ocurre en muchas situaciones.


Escribí esto principalmente para mí, pero espero que a otros les resulte útil :)

Creo que esta explicación es más correcta.Sin embargo, creo que este tratamiento sigue siendo valioso y contemplaremos incorporarlo más adelante. Basta con decir que, cuando la composición de funciones convencionales se ocupa de las funciones de valores simples, las mónadas se tratan de componer funciones que operan en valores de funciones (funciones de orden superior). Cuando se trata de funciones de orden superior (funciones que aceptan o devuelven funciones), la composición debe ser personalizada o hecha a medida para evaluar los operandos cuando se evalúa la composición. Este proceso de evaluación puede ser exótico, como la recopilación de los resultados de procesos asíncronos. No obstante, esta adaptación puede hacerse para seguir un patrón. Una versión de ese patrón se llama la mónada y sigue mucho la adición algebraica. En particular,con respecto al siguiente contenido, tales funciones de orden superior se considerarían operadores matemáticos en la expresión que acepta como operandos otros operadores parcialmente aplicados y, por lo tanto, las funciones, 1+ 2 *, 3 / y 7+ en1+ ( 2* ( 3/ ( 7+ (..) ) ) ) ...

Mónadas abordar un problema que también aparece en la aritmética como la división por cero, DivByZero. Específicamente, los cálculos que involucran división deben detectar o permitir una DivByZeroexcepción. Este requisito hace que la codificación de tales expresiones en el caso general sea confusa.

La solución monádica es abrazar DivByZerohaciendo lo siguiente

  1. Expandir el Numbertipo de incluir DivByZerocomo un valor específico que no es un número regular: NaN, Infinityo Null. Vamos a llamar a este nuevo tipo de número, Nullable<Number>.
  2. Proporcionar una función para "levantar" o envolver un existente Numberen un Nullable<Number>tipo (la idea de "envolver" es que el contenido Numbero valor puede ser "desenvuelto" sin pérdida de información)
  3. Proporcionar una función para "levantar" o envolver a los operadores existentes Numberen una versión que funcione Nullable<Number>. Tal operador "levantado" resultante podría simplemente hacer lo siguiente:
    1. desenvuelva los Nullable<Number>operandos proporcionados y aplique su Numberoperador contenido sobre ellos, luego "levante" el resultado Numberresultante en unNullable<Number>
    2. detectar un DivByZerooperando o una excepción durante la evaluación y pasar una evaluación adicional, produciendo un DivByZerovalor como resultado para afirmar que ( 1 + Null = Null). Sin embargo, las acciones a realizar dependen del programador. En general, estas funciones de envoltorio son donde se escriben muchas de las funcionalidades de las mónadas. La información del estado monádico se mantiene dentro del propio tipo de envoltura desde donde las funciones envueltas inspeccionan y, según el enfoque de inmutabilidad de la programación funcional, construyen un nuevo valor monádico. En el caso de Nullable<Number>, tal información de estado monádica describiría si existe DivByZeroo Numberexiste una real .

Por lo tanto, una Mónada es un tipo expandido junto con una función que "ajusta" el tipo original en esta versión expandida y otra función que envuelve al operador (o operadores) original para que puedan manejar este nuevo tipo expandido. (Las mónadas pueden haber sido una motivación para los genéricos o los parámetros de tipo).

Resulta que, en lugar de limitarse a suavizar el manejo de DivByZero(o Infinity si lo desea), el tratamiento de Monad es ampliamente aplicable a situaciones que pueden beneficiarse de la expansión de tipos para simplificar su codificación. De hecho, esta aplicabilidad parece ser amplia.

Por ejemplo, el IO Monades un tipo que representa el universo, literalmente. La intención es reconocer que los valores devueltos por el HelloWorldprograma prototípico no están completamente descritos por el tipo de resultado stringy su valor "¡Hola mundo!". De hecho, tal resultado también incluye modificaciones a los estados de hardware y memoria de dispositivos como la consola. Por ejemplo, después de la ejecución, la consola ahora muestra texto adicional, el cursor está en una nueva línea, etc. El IO Monades simplemente un reconocimiento explícito de tales efectos externos o efectos secundarios, por así decirlo.

¿Por qué molestarse?

Las mónadas permiten la creación de algoritmos estrictamente sin estado y la documentación de los estados completos. Las máquinas llenas de estado son complejas. Por ejemplo, una máquina con solo 10 bits puede estar en 2 ^ 10 estados posibles. Eliminar la complejidad superflua es el ideal de los lenguajes funcionales.

Las variables mantienen el estado. Eliminar "variables" debería simplemente rellenar. Los programas puramente funcionales no manejan variables, solo valores (a pesar del uso del término 'variable' en la documentación de Haskell) y en su lugar utilizan etiquetas o símbolos o nombres para dichos valores, según sea necesario. En consecuencia, lo más parecido a una variable en un lenguaje puramente funcional son los parámetros recibidos por una función, ya que aceptan nuevos valores en cada invocación. (Una etiqueta se refiere a un valor, mientras que una variable se refiere al lugar donde se mantiene un valor. En consecuencia, puede modificar el contenido de una variable, pero una etiqueta es el contenido en sí. Por último, es mejor recibir una manzana que una bolsa). con una manzana posiblemente en ella.)

La ausencia de variables es la razón por la cual los lenguajes puramente funcionales usan la recursión en lugar de los bucles para iterar. El acto de incrementar un contador implica el uso de una variable que se incrementa y toda la incertidumbre sobre cómo se actualiza, cuándo se prueba, qué valor debe tener y cuándo, y luego la complejidad cuando tiene múltiples subprocesos que potencialmente acceden a ese misma variable

Sin embargo, ¿y qué?

Sin la presencia de un estado, una función debe convertirse en una declaración o una definición de sus resultados, como oposición a una matriculación de algún estado subyacente hacia un resultado. Esencialmente, la expresión funcional de incFun(x) = x + 1es más simple que la expresión imperativa de incImp(x) = x.add(1); return x;Aquí, incFunno se modifica, xpero crea un nuevo valor. incFun puede incluso ser reemplazado por su definición dentro de expresiones como en 1 + incFun(x)convertirse 1 + (x + 1). Por otro lado, incImpmodifica el estado de x. Cualquiera que sea el medio de modificación xpuede ser poco claro y, en última instancia, puede ser imposible de determinar sin ejecutar el programa, además de cualquier problema de concurrencia.

Dicha complejidad se vuelve cognitivamente cara con el tiempo (2 ^ N). Por el contrario, el operador, +, no puede modificar xpero en su lugar debe construir un nuevo valor cuyo resultado se limita a y totalmente determinada por los valores xy 1y la definición de+ . En particular, se evita la explosión de complejidad 2 ^ N. Además, para enfatizar la concurrencia, a incImpdiferencia incFun, no se puede invocar al mismo tiempo sin precauciones para compartir el parámetro, ya que se modifica con cada invocación.

¿Por qué llamarlo una mónada?

Una mónada se caracteriza por una estructura matemática llamada Monoide de la teoría de grupos algebraicos. Dicho esto, todo lo que significa es que un monoide tiene las siguientes tres propiedades:

  1. tiene un operador binario *, tal que x * y = zpor x, y, and zpertenecer a algún tipo S. Por ejemplo, 1 ÷ 2 = 0.5 donde 1, 2 y 0.5 son todos de tipo Number. Cerrado
  2. tiene un elemento de identidad i, asociado con el operador binario que no hace nada como eso (i * x) = (x * i) = x. Por ejemplo, el operador numérico, +, y el número, 0, en 4 + 0 = 0 + 4 = 4. Identidad
  3. el orden de evaluación de "segmentos" es irrelevante: (x * y) * z = x * (y * z). Por ejemplo el operador numérico, +, en (3 + 4) + 12 = 3 + (4 + 12) = 19. Tenga en cuenta, sin embargo, que la secuencia de los términos no debe cambiar. Asociatividad

La propiedad tres (asociatividad) permite evaluar expresiones de longitudes arbitrarias al delinearlas en segmentos y evaluar cada segmento de forma independiente, como en paralelo. Por ejemplo, x1*x2*...*xNpuede ser segmentado en (x1..xJ) * (xJ+1...xM) * (xM+1...xN). El resultado separado x0J * xJM * xMN, puede luego ser recolectado y evaluado adicionalmente de manera similar. El soporte para la segmentación de este tipo es una técnica clave que garantiza la concurrencia correcta y la evaluación distribuida, tal como lo utilizan los algoritmos de búsqueda distribuida de Google (a la carta / reduce).

La propiedad dos (Identidad), permite una mayor facilidad para construir expresiones de varias maneras, aunque puede que no sea del todo evidente; sin embargo, de la misma manera que el cero no era obviamente necesario para los sistemas de conteo temprano, es útil como un concepto de vacío como para envolver un valor vacío. Cabe destacar que en el tipo, la Nullable<Number>, Nullno es un valor vacío, sino más bien DivByZero. Específicamente, nn + DivByZero = DivByZeromientras que nn + 0 = 0 + nn = nn, por lo tanto , 0sigue siendo la identidad bajo +, donde nnes cualquier Nullable<Number>.

Finalmente, hay una razón por la que ya no usamos los números romanos ... no hay adaptaciones ampliadas para cero o fracciones, números irracionales, números negativos, números imaginarios, ... sí, parece que nuestro sistema numérico puede considerarse una mónada.


Las dos cosas que me ayudaron mejor cuando aprendí sobre esto fueron:

Capítulo 8, "Analizadores funcionales", del libro Programación de Graham Hutton en Haskell . En realidad, esto no menciona las mónadas, pero si puede trabajar en el capítulo y entender realmente todo lo que contiene, en particular, cómo se evalúa una secuencia de operaciones de enlace, comprenderá las partes internas de las mónadas. Espera que esto tome varios intentos.

El tutorial Todo sobre Mónadas . Esto da varios buenos ejemplos de su uso, y tengo que decir que la analogía en el Apéndice me funcionó.


Monoid parece ser algo que garantiza que todas las operaciones definidas en un Monoid y un tipo compatible siempre devolverán un tipo compatible dentro del Monoid. Por ejemplo, cualquier número + cualquier número = un número, sin errores.

Mientras que la división acepta dos fracciones y devuelve una fracción, que define la división por cero como Infinito en haskell somewhy (lo que resulta ser una fracción fraccionada en algún)

En cualquier caso, parece que las Mónadas son solo una manera de asegurar que su cadena de operaciones se comporte de una manera predecible, y una función que dice ser Num -> Num, compuesta con otra función de Num-> Num llamada con x no Di, dispara los misiles.

Por otro lado, si tenemos una función que dispara los misiles, podemos componerla con otras funciones que también disparan los misiles, porque nuestra intención es clara: queremos disparar los misiles, pero no intentaremos imprimiendo "Hello World" por alguna extraña razón.

En Haskell, main es de tipo IO (), o IO [()], la distensión es extraña y no lo discutiré, pero esto es lo que creo que sucede:

Si tengo main, quiero que haga una cadena de acciones, la razón por la que ejecuto el programa es para producir un efecto, generalmente a través de IO. Por lo tanto, puedo encadenar las operaciones de IO juntas en main para: hacer IO, nada más.

Si trato de hacer algo que no "devuelve IO", el programa se quejará de que la cadena no fluye, o básicamente "¿Cómo se relaciona esto con lo que estamos tratando de hacer: una acción IO?", Parece forzar el programador debe mantener su línea de pensamiento, sin alejarse y pensar en disparar los misiles, mientras crea algoritmos para clasificar, lo cual no fluye.

Básicamente, las mónadas parecen ser una sugerencia para el compilador de que "hey, ya sabes, esta función que devuelve un número aquí, en realidad no siempre funciona, a veces puede producir un Número, y otras veces Nada, simplemente mantener esto mente". Sabiendo esto, si intenta hacer valer una acción monádica, la acción monádica puede actuar como una excepción de tiempo de compilación que dice "hey, esto no es realmente un número, este PUEDE ser un número, pero no puede asumir esto, haga algo. Para garantizar que el flujo es aceptable ". lo que evita el comportamiento impredecible del programa, en buena medida.

Parece que las mónadas no tienen que ver con la pureza ni el control, sino con el mantenimiento de una identidad de una categoría en la que todo comportamiento es predecible y definido, o no se compila. No puede hacer nada cuando se espera que haga algo, y no puede hacer algo si se espera que no haga nada (visible).

La razón más importante por la que podría pensar en Monads es: ve a ver el código de Procedimientos / OOP, y te darás cuenta de que no sabes dónde comienza ni termina el programa, todo lo que ves es un montón de saltos y muchas matemáticas. , magia, y misiles. No podrá mantenerlo y, si puede, pasará bastante tiempo envolviendo su mente en todo el programa antes de que pueda comprender cualquier parte del mismo, porque la modularidad en este contexto se basa en "secciones" interdependientes. del código, donde el código está optimizado para estar lo más relacionado posible con la promesa de eficiencia / interrelación. Las mónadas son muy concretas y están bien definidas por definición, y aseguran que el flujo del programa sea posible de analizar y aislar partes que son difíciles de analizar, ya que ellas mismas son mónadas. Una mónada parece ser una "unidad comprensible que es predecible a partir de su comprensión total "- Si entiendes la mónada" Tal vez ", no hay forma posible de que haga nada excepto" Tal vez ", que parece trivial, pero en la mayoría de los códigos no monádicos, una función simple" helloworld "puede disparar los misiles, no hacer nada, destruir el universo o incluso distorsionar el tiempo; no tenemos ni idea ni garantía de que ES LO QUE ES. UNA MÓRICA GARANTIZA QUE ES LO QUE ES. Eso es muy poderoso.o destruir el universo o incluso distorsionar el tiempo: no tenemos ni idea ni garantía de que ES LO QUE ES. Una mónada GARANTIZA que ES LO QUE ES. el cual es muy poderosoo destruir el universo o incluso distorsionar el tiempo: no tenemos ni idea ni garantía de que ES LO QUE ES. Una mónada GARANTIZA que ES LO QUE ES. el cual es muy poderoso

Todas las cosas en el "mundo real" parecen ser mónadas, en el sentido de que están limitadas por leyes observables definidas que evitan la confusión. Esto no significa que debamos imitar todas las operaciones de este objeto para crear clases, sino que simplemente podemos decir "un cuadrado es un cuadrado", nada más que un cuadrado, ni siquiera un rectángulo ni un círculo, y "un cuadrado tiene área de la longitud de una de sus dimensiones existentes multiplicada por sí misma. Sin importar el cuadrado que tengas, si es un cuadrado en espacio 2D, el área no puede ser otra cosa que su longitud al cuadrado, es casi trivial demostrarlo. Esto es muy poderoso porque No necesitamos hacer afirmaciones para asegurarnos de que nuestro mundo es como es, solo usamos las implicaciones de la realidad para evitar que nuestros programas se desvíen.

Estoy bastante seguro de que estoy equivocado, pero creo que esto podría ayudar a alguien, así que espero que ayude a alguien.


En términos que un programador OOP entendería (sin ningún fondo de programación funcional), ¿qué es una mónada?

No hay tal explicación que yo sepa, y la sugerencia de que existe existe la arrogancia por parte de los programadores OOP.

¿Qué problema resuelve y cuáles son los lugares más comunes que se utilizan?

Las mónadas resuelven muchos problemas diferentes con una interfaz unificada, por lo que son útiles. Lo principal para lo que se usan es la pereza: permiten funciones perezosas con efectos secundarios, lo que es importante para FFI y similares. También se utilizan comúnmente para el manejo y análisis de errores.

Para aclarar el tipo de comprensión que estaba buscando, digamos que estaba convirtiendo una aplicación de PF que tenía mónadas en una aplicación de POO. ¿Qué harías para trasladar las responsabilidades de las mónadas a la aplicación OOP?

Dado que la pereza no es remotamente común en la POO, en su mayor parte serían irrelevantes. Sin embargo, usted podría portar el manejo de errores monádicos.





haskell functional-programming monads terminology