haskell - significa - que quiere decir monadología
¿Qué es una mónada? (30)
Después de haber mirado brevemente a Haskell recientemente, ¿cuál sería una explicación breve, sucinta y práctica acerca de qué es una mónada en esencia?
He encontrado que la mayoría de las explicaciones que he encontrado son bastante inaccesibles y carecen de detalles prácticos.
En el contexto de la Scala se encuentra lo siguiente es la definición más sencilla. Básicamente flatMap (o se unen) es ''asociativa'' 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 En sentido estricto la definición de una mónada en la programación funcional no es la misma que la definición de una mónada en la teoría de categorías , que se define en las vueltas de map
y flatten
. A pesar de que 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
En la práctica, mónada es una implementación personalizada de la función de operador de composición que se encarga de los efectos secundarios y los valores de entrada y de retorno incompatibles (para encadenamiento).
Monoid parece ser algo que asegura que todas las operaciones definidas en un monoide y un tipo apoyado siempre devolverá un tipo compatible dentro del monoide. Por ejemplo, cualquier número + Cualquier número = Un número, no hay errores.
Considerando división acepta dos fraccionales, y devuelve un fraccional, que define la división por cero como Infinity en somewhy Haskell (que pasa a ser un somewhy fraccional) ...
En cualquier caso, parece mónadas son sólo una manera de asegurarse de que su cadena de operaciones se comporta de una manera predecible, y una función que pretende ser Num -> Num, compuesta con otra función de NUM-> Núm llama con x no lo hace por ejemplo, el fuego de los misiles.
Por otro lado, si tenemos una función que hace disparar los misiles, podemos componer con otras funciones que también disparar los misiles, ya que nuestra intención es clara - queremos disparar los misiles - pero no vamos a tratar imprimir "Hello World" por alguna extraña razón.
En Haskell, la principal es de tipo IO (), o IO [()], el Cabe distinguir extraña y no voy a hablar de ello, pero esto es lo que creo que sucede:
Si tengo principal, yo quiero que haga una cadena de acciones, la razón por la que ejecute el programa es para producir un efecto - por lo general, aunque IO. De este modo puedo operaciones de cadena de IO juntos en principal con el fin de hacer - IO, nada más.
Si trato de hacer algo que no "volver IO", el programa se quejan 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", que aparece a la fuerza el programador para mantener su tren de pensamiento, sin salirse fuera y pensando en disparar los misiles, mientras que la creación de algoritmos para clasificar - que no fluye.
Básicamente, mónadas parecen ser un consejo para el compilador que "bueno, ya sabes esta función que devuelve un número aquí, en realidad no siempre funciona, puede producir a veces un número y, a veces nada en absoluto, sólo tener esto en mente". Sabiendo esto, si se intenta hacer valer una acción monádico, la acción monádico puede actuar como una excepción de tiempo de compilación diciendo "bueno, esto no es realmente un número, esto puede ser un número, pero no se puede suponer esto, hacer algo para asegurar que el flujo es aceptable ". lo que impide que el comportamiento del programa impredecible - en una medida justa.
Parece mónadas no son acerca de la pureza, ni control, sino de mantener una identidad de una categoría en la que es predecible y definido todo el comportamiento, o no se compila. No se puede hacer nada cuando se espera que hacer algo, y no se puede hacer algo si se espera que haga nada (visible).
La razón más grande que podía pensar para Mónadas es - ir a buscar al código / POO procesal, y se dará cuenta de que usted no sabe dónde se inicia el programa, ni extremos, todo lo que ves es una gran cantidad de saltos y una gran cantidad de matemáticas , magia y misiles. Usted no será capaz de mantener, y si se puede, que pasará mucho tiempo envolver su mente alrededor de todo el programa antes de que pueda entender cualquier parte de ella, debido a la modularidad en este contexto se basa en "secciones" interdependiente de código, donde el código se optimiza para ser tan relacionado como sea posible de la promesa de la eficiencia / inter-relación. Mónadas son muy concretos y bien definidos, por definición, y asegurar que el flujo de programa es posible analizar y aislar partes que son difíciles de analizar - como ellos mismos son mónadas. Una mónada parece ser un "unidad comprensible que es predecible a partir de su plena comprensión "- Si usted entiende ''Tal vez'' mónada, no hay manera posible que va a hacer nada más que ser ''Tal vez'', que parece trivial, pero en la mayor parte de código no monádico, una función simple" holamundo "puede disparar los misiles, no hacer nada, o destruir el universo o incluso distorsionar el tiempo -.. no tenemos idea ni tienen ninguna garantía de que es lO qUE eS A garantiza mónada que es lO qUE eS, que es muy potente.o destruir el universo o incluso distorsionar el tiempo - no tenemos idea ni tienen ninguna garantía de que es lo que es. Una mónada garantiza que es lo que es. que es muy potente.o destruir el universo o incluso distorsionar el tiempo - no tenemos idea ni tienen ninguna garantía de que es lo que es. Una mónada garantiza que es lo que es. que es muy potente.
Todas las cosas en el "mundo real" parecen ser mónadas, en el sentido de que está obligado por las leyes observables definidos evitar la confusión. Esto no significa que tengamos que imitar todas las operaciones de este objeto para crear clases, en lugar 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 un área de de la longitud de uno de es existentes dimensiones multiplicado por sí mismo. no importa lo cuadrado que tenga, si se trata de un cuadrado en el espacio 2D, que es el área absolutamente no puede ser otra cosa que su longitud al cuadrado, es casi trivial para probar. Esto es muy poderoso porque no necesitamos hacer afirmaciones para asegurarse de que nuestro mundo es la manera que es, sólo tiene que utilizar implicaciones de la realidad para evitar que nuestros programas se caiga fuera de la pista.
Im casi seguro que estar equivocado, pero creo que esto podría ayudar a alguien por ahí, así que espero que ayude a alguien.
Si he entendido bien, IEnumerable se deriva de las mónadas. Me pregunto si eso podría ser un ángulo interesante de enfoque para aquellos de nosotros desde el mundo # C?
Por lo que vale la pena, aquí hay algunos enlaces a tutoriales que me ayudaron (y no, todavía no he entendido lo que son mónadas).
He estado pensando en mónadas de una manera diferente, últimamente. He estado pensando en ellos como abstraer a cabo la orden de ejecución de forma matemática, lo que hace que nuevos tipos de polimorfismo posible.
Si estás utilizando un lenguaje imperativo, y escribir algunas expresiones en orden, el código siempre se ejecuta exactamente en ese orden.
Y en el caso sencillo, cuando se utiliza una mónada, se siente la misma - se define una lista de expresiones que suceden en orden. Excepto que, en función de la mónada que utilice, el código podría funcionar con el fin (como en la mónada IO), en paralelo a través de varios elementos a la vez (como en la mónada lista), que podría detener un punto intermedio (como en la mónada Maybe) , podría hacer una pausa en un punto intermedio a reanudarse después (como en una mónada Reanudación), puede rebobinar y empezar desde el principio (como en una mónada Transacción), o podría retroceder hasta la mitad de probar otras opciones (como en una mónada lógica) .
Y debido a que las mónadas son polimórficos, es posible ejecutar el mismo código en diferentes mónadas, dependiendo de sus necesidades.
Además, en algunos casos, es posible combinar las mónadas juntos (con transformadores monad) para obtener múltiples funciones al mismo tiempo.
Además de las excelentes respuestas anteriores, permítame ofrecerle un enlace al artículo siguiente (por Patrick Thomson) que explica las mónadas, relacionando el concepto de la biblioteca JavaScript jQuery (y su forma de usar "método de encadenamiento" para manipular el DOM) : jQuery es una Mónada
La documentación de jQuery en sí no se refiere al término "mónada", pero habla de la "Builder", que es probablemente más familiar. Esto no cambia el hecho de que tiene una mónada adecuada puede haber sin siquiera darse cuenta.
Las dos cosas que mejor me ayudaron al aprender sobre existían:
Capítulo 8, "analizadores funcionales," del libro de Graham Hutton programación en Haskell . Esto no menciona en absoluto mónadas, en realidad, pero si se puede trabajar a través de capítulo y realmente entender todo lo que contiene, en particular, cómo se evalúa una secuencia de operaciones de enlace, vas a entender el funcionamiento interno de las mónadas. Esperamos que esto lleve 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 Appendex que trabajó para mí.
Una mónada es una forma de combinar juntos los cálculos que comparten un contexto común. Es como construir una red de tuberías. Cuando la construcción de la red, no hay datos que fluyen a través de él. Pero cuando he terminado de juntar las piezas todos los bits junto con ''bind'' y ''retorno'', entonces invoco algo así runMyMonad monad data
y los datos fluyen a través de las tuberías.
Después de mucho esfuerzo, creo que por fin entiendo la mónada. Después de releer mi propia extensa crítica de la parte superior votado abrumadoramente respuesta, voy a ofrecer esta explicación.
Hay tres preguntas que necesitan ser contestadas para entender mónadas:
¿Por qué necesita una mónada? ¿Qué es una monada? ¿Cómo se implementa una mónada?
Como señalé en mis comentarios originales, demasiadas explicaciones monad quedan atrapados en la pregunta número 3, sin, y antes de que realmente cubre adecuadamente la pregunta 2, o la pregunta 1.
¿Por qué necesita una mónada?
lenguajes funcionales puros como Haskell son diferentes de los lenguajes imperativos como C o Java en eso, un programa funcional puro no se ejecutan necesariamente en un orden específico, un paso a la vez. Un programa Haskell es más parecido a una función matemática, en la que es posible resolver la "ecuación" en cualquier número de órdenes posibles. Esto confiere una serie de ventajas, entre las cuales es que elimina la posibilidad de que ciertos tipos de insectos, en particular las relativas a cosas como "estado".
Sin embargo, hay ciertos problemas que no son tan fáciles de resolver con este estilo de programación. Algunas cosas, como la programación de la consola, y el archivo de E / S, necesitan cosas que suceden en un orden determinado, o la necesidad de mantener el estado. Una forma de hacer frente a este problema es crear una especie de objeto que representa el estado de un cálculo, y una serie de funciones que toman un objeto de estado como entrada y devuelve un nuevo objeto de estado modificado.
así que vamos a crear un hipotético valor de "estado", que representa el estado de una pantalla de la consola. exactamente cómo se construye este valor no es importante, pero digamos que es un conjunto de caracteres ASCII de longitud de bytes que representa lo que es actualmente visible en la pantalla, y una matriz que representa la última línea de la entrada introducida por el usuario, en pseudocódigo. Hemos definido algunas funciones que toman estado de la consola, lo modifican, y devuelven un nuevo estado de la consola.
consolestate MyConsole = new consolestate;
por lo que hacer la programación de la consola, pero de una manera funcional puro, lo que se necesita para anidar una gran cantidad de llamadas a funciones dentro de 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 que obliga a cambios en la consola que sucedan en un orden determinado. Pero, probablemente querremos hacer algo más que sólo unas pocas operaciones en un momento como en el ejemplo anterior. funciones de anidación de esa manera comenzarán a ser torpe. Lo que queremos, es el código que hace esencialmente lo mismo que el anterior, pero se escribe un poco más como esto:
consolestate FinalConsole = myconsole:
print("Hello, what''s your name?"):
input():
print("hello, %inputbuffer%!");
este hecho sería una manera más conveniente para escribirlo. ¿Cómo podemos hacer que sin embargo?
¿Qué es una monada?
una vez que tenga un tipo (como consolestate
) que defina junto con un montón de funciones diseñadas específicamente para operar en ese tipo, puede activar el paquete entero de estas cosas en una "mónada" mediante la definición de un operador como :
(enlazar) que automáticamente alimenta valores de retorno a su izquierda, en parámetros de función en su derecho, y un lift
operador que convierte funciones normales, en las funciones que trabajan con ese tipo específico de operador se unen.
¿Cómo se implementa una mónada?
Ver otras respuestas, que parecen bastante libre para saltar en los detalles de eso.
Mónadas no son metáforas , sino una abstracción útil en la práctica que emerge de un patrón común, como explica Daniel Spiewak.
Al explicar las mónadas parece ser como explicar las declaraciones de flujo de control. Imagine que un programador no le pide que explique ellos?
Se les puede dar una explicación que implica la teoría - la lógica booleana, registrar valores, punteros, pilas, y marcos. Pero eso sería una locura.
Se podría explicar en términos de la sintaxis. Básicamente todos los estados de flujos de control en C tienen llaves, y se puede distinguir el estado y el código condicional por dónde están en relación a los soportes. Eso puede ser aún más loco.
O también podría explicar bucles, si las declaraciones, rutinas, subrutinas y posiblemente co-rutinas.
Mónadas pueden sustituir a un número bastante grande de técnicas de programación. Hay una sintaxis específica en idiomas que los apoyan, y algunas teorías acerca de ellos.
Son también una manera para que los programadores funcionales para utilizar el código imperativo sin tener que admitirlo, pero ese no es su único uso.
Mi tutorial favorito Mónada:
http://www.haskell.org/haskellwiki/All_About_Monads
(De un total de 170.000 visitas en una búsqueda en Google de "tutorial mónada"!)
@Stu: El punto de mónadas es permitir que usted agregue (por lo general) la semántica secuenciales de códigos a pura de otra manera; incluso se puede componer mónadas (utilizando Monad Transformers) y obtener más interesantes y complicados semántica combinados, como el análisis de la gestión de errores, estado compartido, y la explotación forestal, por ejemplo. Todo esto es posible en código puro, mónadas sólo le permiten abstraer a la basura y volver a utilizarlo en las bibliotecas modulares (siempre es bueno en la programación), así como proporcionar la sintaxis conveniente para que se vea imprescindible.
Haskell ya tiene la sobrecarga de operadores [1]: se utiliza el tipo clases mucho la forma en que se podría utilizar interfaces en Java o C #, pero Haskell sólo pasa a permitir que también los tokens no alfanuméricos como && + y> como identificadores infijos. Es sólo la sobrecarga de operadores en su forma de ver que si se refiere a la "sobrecarga del punto y coma" [2]. Suena como magia negro y pidiendo problemas de "sobrecarga del punto y coma" (imagen emprendedores hackers Perl se entere 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 secuenciación explícita.
Todo esto suena mucho más complicado de lo que necesita. El artículo de SIGFPE está muy bien, pero utiliza Haskell para explicarlo, qué tipo de falla para romper el problema de la gallina y el huevo de entender Haskell asimilar mónadas y la comprensión de las mónadas a asimilar Haskell.
[1] Esta es una cuestión independiente de mónadas pero mónadas usar la función de sobrecarga de operadores de Haskell.
[2] Esta es también una simplificación excesiva ya que el operador para encadenar acciones monádicos se >> = (pronunciado "atar"), pero no es azúcar sintáctico ( "hacer") que le permite utilizar los apoyos y puntos y comas y / o sangría y saltos de línea.
[Descargo de responsabilidad: todavía estoy tratando de asimilar completamente las mónadas. Lo siguiente es exactamente lo que he entendido hasta ahora. Si está mal, con suerte alguien con conocimiento me llamará por la alfombra.]
Arnar escribió:
Las mónadas son simplemente una forma de envolver cosas y proporcionar métodos para realizar operaciones sobre las cosas envueltas sin desenvolverlas.
Eso es precisamente eso. La idea es así:
Tomas algún tipo de valor y lo envuelves con información adicional. Al igual que el valor es de cierto tipo (por ejemplo, un entero o una cadena), la información adicional es de cierto tipo.
Por ejemplo, esa información adicional podría ser un
Maybe
o unIO
.Luego tiene algunos operadores que le permiten operar con los datos envueltos mientras lleva esa información adicional. Estos operadores usan la información adicional para decidir cómo cambiar el comportamiento de la operación en el valor envuelto.
Por ejemplo, un
Maybe Int
puede ser unJust Int
oNothing
. Ahora, si agrega unMaybe Int
aMaybe Int
, el operador verificará si ambos están dentro deJust Int
., Y si es así, desenvolverá elInt
, les pasará el operador de suma, volverá a envolver elInt
resultante. en un nuevoJust Int
(que es unMaybe Int
válido), y así devolver unMaybe Int
. Pero si uno de ellos eraNothing
, este operador simplemente devolveráNothing
, que nuevamente es unMaybe Int
válido. De esta manera, puede pretender que susMaybe Int
s son solo números normales y realizar operaciones matemáticas regulares sobre ellos. Si obtuvieras unNothing
, tus ecuaciones seguirán produciendo el resultado correcto, sin tener que tirar basura porNothing
todas partes .
Pero el ejemplo es exactamente lo que sucede para Maybe
. Si la información adicional fuera un IO
, se llamaría entonces a ese operador especial definido para IO
, y podría hacer algo totalmente diferente antes de realizar la adición. (OK, agregar dos IO Int
juntos probablemente sea absurdo, todavía no estoy seguro). (Además, si prestaste atención al ejemplo Maybe
, habrás notado que "ajustar 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" aproximadamente significa "patrón" . Pero en lugar de un libro lleno de patrones informalmente explicados y específicamente denominados, ahora tiene una construcción de lenguaje (sintaxis y todo) que le permite declarar nuevos patrones como cosas en 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 las personas encuentran las mónadas tan confusas: porque son un concepto tan genérico. Preguntar qué hace que algo sea una mónada 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 de la Banda de los Cuatro y memorizar la construcción de un patrón particular, usted simplemente escribe el código que implementa este patrón en un agnóstico, forma genérica una vez y luego has terminado! A continuación, puede reutilizar este patrón, como Visitor, Strategy o Faade o lo que sea, simplemente decorando las operaciones en su código con él, ¡sin tener que volver a implementarlo una y otra vez!
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 esnobs intelectuales se enorgullezcan de comprender (OK, eso también, por supuesto, teehee), pero en realidad hace que el código sea más simple.
Pero, ¡podrías haber inventado Mónadas!
Sigfpe dice:
Pero todos estos introducen las mónadas como algo esotérico que necesita explicación. Pero lo que quiero decir es que no son esotéricos en absoluto. De hecho, frente a varios problemas en la programación funcional, usted habría sido conducido, inexorablemente, a ciertas soluciones, todas las cuales son ejemplos de mónadas. De hecho, espero hacerte que los inventes ahora si aún no lo hiciste. Es entonces un pequeño paso darse cuenta de que todas estas soluciones son, de hecho, la misma solución disfrazada. Y después de leer esto, es posible que esté en una mejor posición para comprender otros documentos sobre 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 tema de los efectos secundarios. Entonces comenzaremos con ellos. (Tenga en cuenta que las mónadas le permiten hacer algo más que manejar los efectos secundarios, en particular, muchos tipos de objetos contenedores pueden verse como mónadas. Algunas de las introducciones a las mónadas encuentran difícil conciliar estos dos usos diferentes de las mónadas y concentrarse en solo uno 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 único argumento de coma flotante y devuelve un resultado de punto flotante. Superficialmente, puede parecer un poco como una función matemática mapeando reales a reales, pero una función de C ++ puede hacer más que simplemente devolver un número que depende de sus argumentos. Puede leer y escribir los valores de variables globales, así como escribir resultados en la pantalla y recibir información del usuario. En un lenguaje funcional puro, sin embargo, 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.
Una mónada es un tipo de datos que tiene dos operaciones: >>=
(aka bind
) y return
(aka unit
). return
toma un valor arbitrario y crea una instancia de la mónada con él. >>=
toma una instancia de la mónada y mapea una función sobre ella. (Ya puede ver que una mónada es un tipo extraño de tipo de datos, ya que en la mayoría de los lenguajes de programación no se puede escribir una función que tome un valor arbitrario y se cree un tipo.) Las mónadas usan un tipo de polimorfismo paramétrico ).
En la notación Haskell, la interfaz de la mónada está escrita
class Monad m where
return :: a -> m a
(>>=) :: forall a b . m a -> (a -> m b) -> m b
Se supone que estas operaciones obedecen a ciertas "leyes", pero eso no es terriblemente importante: las "leyes" solo codifican la forma en que deben comportarse las implementaciones sensatas de las operaciones (básicamente, que >>=
y el return
deben ponerse de acuerdo sobre cómo se transforman los valores en instancias de mónada y que >>=
es asociativo).
Las mónadas no son solo acerca del estado y la E / S: resumen un patrón común de computación que incluye trabajar con estado, E / S, excepciones y no determinismo. Probablemente las mónadas más simples de entender sean listas y tipos de opciones:
instance Monad [ ] where
[] >>= k = []
(x:xs) >>= k = k x ++ (xs >>= k)
return x = [x]
instance Monad Maybe where
Just x >>= k = k x
Nothing >>= k = Nothing
return x = Just x
donde []
y :
son los constructores de listas, ++
es el operador de concatenación, y Just
y Nothing
son los constructores Maybe
. Ambas mónadas encapsulan patrones comunes y útiles de computación en sus respectivos tipos de datos (tenga en cuenta que ninguno de los dos tiene nada que ver con efectos secundarios o E / S).
Realmente tienes que jugar a escribir un código Haskell no trivial para apreciar de qué mónadas se trata y por qué son útiles.
Primero debes entender qué es un functor. Antes de eso, entiende las funciones de orden superior.
Una función de orden superior es simplemente una función que toma una función como argumento.
Un funtor es cualquier construcción de tipo T
para la cual existe una función de orden superior, llamada map
, que transforma una función de tipo a -> b
(dados 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 los x
, p
y q
(notación Haskell):
map id = id
map (p . q) = map p . map q
Por ejemplo, un constructor de tipos llamado List
es un funtor si viene equipado con una función de tipo (a -> b) -> List a -> List b
que obedece a las leyes anteriores. La única implementación práctica es obvia. La función List a -> List b
resultante itera sobre la lista dada, invocando la función (a -> b)
para cada elemento y devuelve la lista de resultados.
Una mónada es esencialmente solo un functor T
con dos métodos extra, join
, de tipo T (T a) -> T a
, y 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 eso útil? Porque podría, por ejemplo, map
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.
Puede escribir una función que se map
, luego se join
. Esta función se llama bind
, o flatMap
, o (>>=)
, o (=<<)
. Esta es normalmente la forma en que se da una instancia de mónada en Haskell.
Una mónada tiene que cumplir 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 que join (pure x) == x
.
Primero: el término mónada es un poco vacío si no eres un matemático. Un término alternativo es el generador de cálculos que es un poco más descriptivo de lo que realmente son útiles.
Usted pide ejemplos prácticos:
Ejemplo 1: comprensión de la lista :
[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 lista de mónadas. 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 usan mónadas, constructores de computación AKA. El tema común es que la mónada encadena operaciones de alguna manera específica y útil. En la lista de comprensión, las operaciones están encadenadas de modo que si una operación devuelve una lista, se realizan las siguientes operaciones en cada elemento de la lista. La mónima IO, por otro lado, realiza las operaciones secuencialmente, pero pasa una "variable oculta" a lo largo, que representa "el estado del mundo", que nos permite escribir código de E / S de una manera puramente funcional.
Resulta que el patrón de operaciones de encadenamiento es bastante útil y se usa para muchas cosas diferentes en Haskell.
Otro ejemplo son las excepciones: al usar la mónada Error
, las operaciones se encadenan de manera tal que se realizan de forma secuencial, excepto si se produce un error, en cuyo caso el resto de la cadena se abandona.
Tanto la sintaxis de comprensión de lista como la notación de do son azúcares sintácticos para operaciones de encadenamiento utilizando el operador >>=
. Una mónada es básicamente solo 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 de forma secuencial hasta que falla una coincidencia, en cuyo caso la mónada retrocede a la última <|>
e intenta la siguiente opción. De nuevo, una forma de encadenar operaciones con alguna semántica adicional y útil.
Ejemplo 4: Programación asincrónica
Los ejemplos anteriores están en Haskell, pero resulta que F # también admite 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 busca una página web. La línea de golpe es el uso de GetResponseAsync
: realmente espera la respuesta en un hilo separado, mientras que el hilo principal regresa de la función. Las últimas tres líneas se ejecutan en el subproceso generado cuando se recibe la respuesta.
En la mayoría de los demás idiomas, debería crear explícitamente una función separada para las líneas que manejan la respuesta. La mónada async
es capaz de "dividir" el bloque por sí misma 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 todas estas cosas elegantes de control de flujo? Lo que sucede realmente en un do-block (o una expresión de cálculo como se llaman en F #), es que cada operación (básicamente cada línea) se envuelve en una función anónima separada. Estas funciones se combinan utilizando el operador bind
(deletreado >>=
en Haskell). Dado que la operación de bind
combina funciones, puede ejecutarlas como lo considere oportuno: secuencialmente, varias veces, al revés, descartar algunas, ejecutar algunas en una secuencia separada cuando se lo parezca 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 está sucediendo realmente. 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 luego tomado por el próximo operador >>=
y nuevamente combinado con una función para producir un nuevo valor. >>=
puede verse 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 tienen que ser del tipo de la misma mónada, de lo contrario, el operador >>=
no funcionará).
La implementación más simple posible de >>=
simplemente 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 extra en el proceso. la implementación de mónada de >>=
.
Existe cierta habilidad adicional en la forma en que los valores pasan de una operación a la siguiente, pero esto requiere una explicación más profunda del sistema de tipo Haskell.
Resumiendo
En Haskell-terms, una mónada es un tipo parametrizado que es una instancia de la clase de tipo Monad, que define >>=
junto con algunos otros operadores. En términos simples, una mónada es simplemente un tipo para el cual se define la operación >>=
.
En sí mismo >>=
es solo una forma engorrosa de encadenar funciones, pero con la presencia de la notación do que oculta la "fontanería", las operaciones monádicas resultan ser una abstracción muy útil y útil, útiles en muchos lugares del lenguaje y útil para crear sus propios mini-idiomas en el idioma.
¿Por qué las mónadas son duras?
Para muchos estudiantes Haskell, las mónadas son un obstáculo que golpean como una pared de ladrillos. No es que las mónadas en sí mismas sean complejas, sino que la implementación se basa en muchas otras características avanzadas de Haskell como tipos parametrizados, clases de tipos, etc. El problema es que Haskell I / O está basado en mónadas, y las E / S son probablemente una de las primeras cosas que quieres entender cuando aprendes un nuevo idioma. Después de todo, no es muy divertido crear programas que no producen ningún salida. No tengo una solución inmediata para este problema de huevo y gallina, excepto tratar 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
Explicar "qué es una mónada" es como decir "¿qué es un número?" Usamos números todo el tiempo. Pero imagina que conociste a alguien que no sabía nada sobre números. ¿Cómo demonios explicarías qué números son? ¿Y cómo comenzarías a describir por qué podría ser útil?
¿Qué es una mónada? La respuesta corta: es una forma específica de encadenar operaciones juntas.
Básicamente, está escribiendo pasos de ejecución y vinculándolos con la "función de enlace". (En Haskell, se llama >>=
.) Puede escribir las llamadas al operador de enlace usted mismo, o puede usar el azúcar de sintaxis que hace que el compilador inserte esas llamadas de función para usted. Pero de cualquier manera, cada paso está separado por una llamada a esta función de enlace.
Entonces la función de enlace es como un punto y coma; separa los pasos en un proceso. El trabajo de la función de enlace es tomar el resultado del paso anterior y alimentarlo en el próximo paso.
Eso no suena demasiado duro, ¿verdad? Pero hay más de un tipo de mónada. ¿Por qué? ¿Cómo?
Bueno, la función de enlace solo puede tomar el resultado de un paso y alimentarlo al próximo paso. Pero si eso es "todo", la mónada lo hace ... eso en realidad no es muy útil. Y eso es importante de entender: cada mónada útil hace algo más además de ser solo una mónada. Cada mónada útil tiene un "poder especial", lo que la hace única.
(Una mónada que no hace nada especial se llama "mónada de identidad". Más bien como la función de identidad, esto suena como una cosa totalmente sin sentido, pero resulta que no es ... Pero esa es otra historia ™.)
Básicamente, cada mónada tiene su propia implementación de la función bind. Y puede escribir una función de vinculación de manera que no moleste entre los pasos de ejecución. Por ejemplo:
Si cada paso devuelve un indicador de éxito / falla, puede hacer que bind ejecute el siguiente paso solo si el anterior tuvo éxito. De esta forma, un paso fallido cancela la secuencia completa "automáticamente", sin que usted realice ninguna prueba condicional. (La falla mónada )
Extendiendo esta idea, puede implementar "excepciones". ( Monad de error o Monad de excepción ). Como los está definiendo usted mismo en lugar de ser una función de idioma, puede definir cómo funcionan. (Por ejemplo, quizás quieras ignorar las dos primeras excepciones y abortar solo cuando se lanza una tercera excepción).
Puede hacer que cada paso devuelva resultados múltiples , y hacer que la función de vinculación se repita sobre ellos, y alimentar a cada uno en el próximo paso para usted. De esta forma, no tiene que seguir escribiendo bucles por todos lados cuando se trata de resultados múltiples. La función de enlace "automáticamente" hace todo eso por usted. (La lista Mónada )
Además de pasar un "resultado" de un paso a otro, puede hacer que la función de enlace también pase datos adicionales . Esta información ahora no aparece en su código fuente, pero aún puede acceder a ella desde cualquier lugar, sin tener que pasarla manualmente a cada función. (The Reader Monad .)
Puede hacerlo para que los "datos adicionales" puedan ser reemplazados. Esto le permite simular actualizaciones destructivas , sin hacer realmente actualizaciones destructivas. (La Mónada del Estado y su primo el escritor Mónada ).
Debido a que solo estás simulando actualizaciones destructivas, puedes hacer trivialmente cosas que serían imposibles con actualizaciones destructivas reales . Por ejemplo, puede deshacer la última actualización o volver a una versión anterior .
Puede hacer una mónada donde los cálculos se pueden pausar , por lo que puede pausar su programa, acceder y modificar los datos de estado internos, y luego reanudarlo.
Puede implementar "continuaciones" como una mónada. ¡Esto te permite romper las mentes de las personas!
Todo esto y más es posible con mónadas. Por supuesto, todo esto también es perfectamente posible sin mónadas también. Simplemente es drásticamente más fácil usar mónadas.
(Ver también las respuestas en ¿Qué es una mónada? )
Una buena motivación para Monads es sigfpe (Dan Piponi) ¡ Podrías haber inventado mónadas! (Y tal vez ya lo haya hecho) . Hay MUCHOS otros tutoriales de mónadas , muchos de los cuales intentan explicar incorrectamente las mónadas en "términos simples" usando varias analogías: esta es la falacia de la mónada tutorial ; Evítales.
Como dice DR MacIver en Cuéntanos por qué tu lenguaje apesta :
Entonces, las cosas que odio de Haskell:
Comencemos con lo obvio. Monad tutoriales. No, no mónadas. Específicamente los tutoriales. Son infinitos, exagerados y queridos Dios, son tediosos. Además, nunca he visto ninguna evidencia convincente de que realmente ayuden. Lee la definición de la clase, escribe un código, supera el nombre aterrador.
¿Dices que entiendes la Mónada Maybe? Bien, estás en camino. Simplemente comience a usar otras mónadas y tarde o temprano comprenderá qué mónadas son en general.
[Si está matemáticamente orientado, es posible que desee ignorar las docenas de tutoriales y aprender la definición, o seguir las clases en la teoría de categorías :) La parte principal de la definición es que una Monad M implica un "constructor de tipo" que define para cada tipo existente "T" un nuevo tipo "MT", y algunas formas de ir y venir entre tipos "regulares" y "M".]
Además, sorprendentemente, una de las mejores introducciones a las mónadas es en realidad uno de los primeros trabajos académicos que introdujeron mónadas, las Mónadas de Philip Wadler para la programación funcional . En realidad tiene ejemplos prácticos, no triviales de motivación, a diferencia de muchos de los tutoriales artificiales que existen.
Escribí esto sobre todo para mí, pero espero que a otros les resulta útil :)
Creo que esta explicación es más correcto. Sin embargo, creo que este tratamiento sigue siendo valioso y contemplará incorporarlo en un momento posterior. Baste decir, mientras que la función acuerdo convencional composición con las funciones que operan en valores de objeto, mónadas trata de proporcionar la composición de funciones que operan sobre los valores de función. Cuando se trata de unas funciones de orden superior (funciones que acepte o funciones devuelven), la composición debe ser personalizar. Por el artículo anterior, ese es el propósito de las mónadas. Con respecto a los siguientes contenidos tales funciones de orden superior sería con respecto a los operadores matemáticos como funciones aceptando versión parcialmente aplicada de sí mismos 1+ ( 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 incluyen la división deben detectar o permitir una DivByZero
excepción. Este requisito hace que la codificación de dichas expresiones en el caso general desordenado.
La solución Monádica es abrazar DivByZero
al hacer lo siguiente
- Expandir el
Number
tipo de incluirDivByZero
como un valor específico que no es un número regular:NaN
,Infinity
oNull
. Vamos a llamar a este nuevo tipo de número,Nullable<Number>
. - Proporcionar una función para "levantar" o envolver una ya existente
Number
en unNullable<Number>
tipo (la idea de "envoltura" es que el contenidoNumber
o valor puede ser "envolver" sin pérdida de información) - Proporcionar una función de "elevación" o envolver los operadores existentes en
Number
en unas versiones que opera sobreNullable<Number>
. Una resultante operador Tal "levantado" meramente podría hacer lo siguiente:- desenvolver proporcionados
Nullable<Number>
operandos y aplicar su contenidoNumber
operador sobre ellos a continuación, "levantar" la resultanteNumber
resultado en unaNullable<Number>
- detectar un
DivByZero
operando o excepción durante la evaluación y por pasar una evaluación adicional, produciendo unDivByZero
valor como el resultado afirmar que (1 + Null = Null
). Sin embargo, las acciones a tomar depende del programador. En general, estas funciones de contenedor son en gran parte de la funcionalidad de las mónadas están escritos. La información de estado monádico se mantiene dentro del propio tipo de envoltura desde donde las funciones envueltos inspeccionar y, por enfoque inmutabilidad programación funcional, construir un nuevo valor monádico. En el caso deNullable<Number>
una información de estado tales monádico describiría siDivByZero
o un realNumber
existe.
- desenvolver proporcionados
Por lo tanto, una mónada es un tipo expandida junto con una función que "se devuelve" el tipo original en esta versión ampliada y otra función que envuelve el operador original (s) para que puedan manejar este nuevo tipo expandida. (Mónadas puede haber habido una motivación para genéricos o de tipo-parámetros.)
Resulta que en lugar de simplemente suavizando el manejo de DivByZero
(o infinito si se quiere), el tratamiento mónada es ampliamente aplicable a situaciones que pueden beneficiarse de la expansión tipo para simplificar su codificación. De hecho, esta aplicación parece ser amplia.
Por ejemplo, el IO Monad
es un tipo que representa el universo, literalmente. La intención es reconocer que los valores devueltos por el prototipo de HelloWorld
programa no se describe por completo por el tipo de resultado string
y su valor "Hello World!". De hecho, este resultado también incluye modificaciones a los estados de hardware y de memoria de dispositivos como la consola. Por ejemplo, después de la ejecución de la consola muestra ahora un texto adicional, el cursor se encuentra en una nueva línea, y así sucesivamente. El IO Monad
no es más que un reconocimiento explícito de estos efectos externos o efectos secundarios, si se quiere.
¿Por qué molestarse?
Mónadas permiten algoritmos estrictamente sin estado que se elaboren y documentar los llenos de estado. máquinas para todo el Estado son complejas. Por ejemplo, una máquina con sólo 10 bits puede estar en 2 ^ 10 estados posibles. La eliminación de la complejidad superflua es el ideal de los lenguajes funcionales.
Las variables de mantenimiento de estado. La eliminación de las "variables" debe simplemente cosas. Puramente funcionales programas no manejan variables, sólo los valores (a pesar uso del término ''variable'' en la documentación de Haskell) y en lugar de utilizar etiquetas o símbolos o nombres de tales valores, según sea necesario. En consecuencia, lo más parecido a una variable en un lenguaje puramente funcional es los parámetros recibidos por una función que acepten nuevos valores en cada invocación. (Una etiqueta se refiere a un valor, mientras que una variable se refiere al lugar donde se celebra un valor. En consecuencia, se puede modificar el contenido de una variable, pero una etiqueta es el contenido en sí. Último es mejor que debe darse una manzana que una bolsa posiblemente con una manzana en ella).
La ausencia de variables es la razón por idiomas puramente funcionales utilizan recursividad en lugar de bucles para repetir. El acto de incrementar un contador implica el uso de una variable que se convierte incrementa y toda la incertidumbre con la forma en que ésta se actualiza, cuando se hace la prueba, ¿qué valor debe ser y cuándo, y luego la complejidad cuando se tienen varios hilos potencialmente acceder a ese misma variable.
Sin embargo, ¿Y qué?
Sin la presencia del estado, una función debe convertirse en una declaración o una definición de la misma de resultados, como se oponen a una matrícula de algún estado subyacente hacia un resultado. En esencia, la expresión funcional de incFun(x) = x + 1
es más simple que la expresión imperativo de incImp(x) = x.add(1); return x;
aquí, incFun
no modifica x
sino que 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 otra parte, incImp
modifica el estado de x
. Lo que significa que dicha modificación para x
puede no estar claro y, finalmente, puede ser imposible determinar sin ejecutar el programa, además de cualquier problemas de concurrencia.
Esta complejidad se cognitivamente costoso en el tiempo (2 ^ n). Por el contrario, el operador, +
, no puede modificar x
pero en su lugar debe construir un nuevo valor cuyo resultado se limita a y totalmente determinada por los valores x
y 1
y la definición de+
. En particular, se evita la explosión complejidad 2 ^ N. Además, para enfatizar la concurrencia, incImp
a diferencia incFun
, no puede ser invocado simultáneamente sin las precauciones de todo el reparto del parámetro, ya que se convierte modificado por cada invocación.
¿Por qué llamarlo una mónada?
A mónada se caracteriza por una estructura matemática llamada un Monoid de la teoría de grupos algebraica. Dicho esto, todo lo que significa es que monoide tiene las siguientes tres propiedades:
- tiene un operador binario,
*
de tal manera quex * y = z
parax, y, and z
pertenecer a un cierto tipoS
. Por ejemplo 1 ÷ 2 = 0,5, donde 1, 2, y 0,5 son todos de tipoNumber
. Cerrado - tiene un elemento de identidad,
i
asociada con el operador binario que no hace nada tal que(i * x) = (x * i) = x
. Por ejemplo, el operador numérico, +, y el número, 0, en 4 + 0 = 0 + 4 = 4. Identidad - 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 secuenciación de los términos no debe cambiar. asociatividad
Propiedad tres (asociatividad) permite expresiones de longitudes arbitrarias a ser evaluados por delinear ellos en segmentos y la evaluación de cada segmento de forma independiente tal como en paralelo. Por ejemplo, x1*x2*...*xN
puede ser segmentado en (x1..xJ) * (xJ+1...xM) * (xM+1...xN)
. El resultado separada, x0J * xJM * xMN
, puede entonces ser recogida y evaluó adicionalmente de manera similar. El apoyo a la segmentación de este tipo es una técnica clave garantizar la concurrencia y la evaluación correcta distribuido usadas por los algoritmos de búsqueda distribuidos de Google (al estilo de mapa / reducir).
Propiedad de dos (Identidad), permite una mayor facilidad en la construcción de expresiones de diversas maneras aunque puede no ser del todo evidente; Sin embargo, de la misma manera que el cero no era evidente la necesidad de sistemas de conteo de los primeros es útil como un concepto de vacío como en envolver a un valor vacío. Cabe destacar que en el tipo, la Nullable<Number>
, Null
no es un valor vacío, sino más bien DivByZero
. Específicamente, nn + DivByZero = DivByZero
mientras que nn + 0 = 0 + nn = nn
, por lo tanto, 0
sigue siendo la identidad bajo +
, donde nn
es cualquier Nullable<Number>
.
Por último, hay una razón por la que usamos `t números romanos nunca más ... no hay alojamiento ampliado para cero o fracciones, números irracionales, los números negativos, los números imaginarios, ... sí, parece que nuestro sistema de numeración se puede considerar una mónada.
Princesa explicación ''s de F # Computación Expresiones me ayudó, aunque todavía no puedo decir que realmente he entendido.
EDITAR : esta serie - explicar las mónadas con javascript - es el que ''inclinó la balanza'' para mí.
http://blog.jcoglan.com/2011/03/06/monad-syntax-for-javascript/
http://blog.jcoglan.com/2011/03/11/promises-are-the-monad-of-asynchronous-programming/
Creo que la comprensión de las mónadas es algo que se toma por sorpresa. En ese sentido, la lectura como muchos ''tutoriales'' como usted puede es una buena idea, pero a menudo cosas extrañas (idioma desconocido o sintaxis) impide que el cerebro pueda concentrarse en lo esencial.
Algunas de las cosas que tenía dificultad para comprender:
- Explicaciones basadas en reglas nunca trabajó para mí, porque los ejemplos más prácticos realmente requieren más que un simple retorno / vinculación.
- Además, llamándolos reglas no ayudaron. Es más un caso de "éstas son las cosas que tienen algo en común, vamos a llamar a las cosas '''' mónadas, y los bits de ''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 ela
deM<a>
fin de pasarlo aa -> M<b>
. No creo que haya leído en cualquier lugar (tal vez es obvio para todos los demás), que la inversa de Retorno (M<a> -> a
) tiene que existir en el interior de la mónada, simplemente no necesita ser expuesta.
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 sobre las cosas envueltas sin desenvolverlas.
Por ejemplo, puede crear un tipo para ajustar otro, en Haskell:
data Wrapped a = Wrap a
Para envolver cosas definimos
return :: a -> Wrapped a
return x = Wrap x
Para realizar operaciones sin desenvolver, digamos que tiene una función f :: a -> b
, puede hacer esto para levantar esa función para actuar sobre valores envueltos:
fmap :: (a -> b) -> (Wrapped a -> Wrapped b)
fmap f (Wrap x) = Wrap (f x)
Eso es todo lo que hay para 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 solo en términos de bind
y return
. Entonces, cuando se define una mónada ... se da su tipo (aquí estaba Wrapped a
) y luego se 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 todos lados, encapsular el estado de una manera pura es solo uno de ellos.
Para un buen artículo sobre cómo las mónadas se pueden utilizar para introducir dependencias funcionales y, por lo tanto, controlar el orden de la 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. Lea sobre ellos lo que le parezca interesante y no se preocupe si no comprende de inmediato. Entonces solo bucear en un idioma como Haskell es el camino a seguir. Las mónadas son una de estas cosas en las que la comprensión se cuela en tu cerebro mediante la práctica; un día, de repente te das cuenta de que las entiendes.
Lo que el mundo necesita es otra entrada del blog mónada, pero creo que esto es útil en la identificación de las mónadas existentes en la naturaleza.
Lo anterior es un fractal de Sierpinski llamado triángulo, el único fractal puedo recordar a dibujar. Los fractales son estructura auto-similar como el triángulo de arriba, en la que las partes son similares a la totalidad (en este caso exactamente la mitad de la escala como el triángulo de los padres).
Mónadas son fractales. Teniendo en cuenta una estructura de datos monádico, sus valores se pueden componer para formar otro valor de la estructura de datos. Esto es por eso que es útil para la programación, y es por ello que occurrs en muchas situaciones.
En el Coursera "Principios de programación reactiva" formación - Erik Meier los describe como:
"Monads are return types that guide you through the happy path." -Erik Meijer
Esta respuesta comienza con un ejemplo motivador, funciona a través del ejemplo, se deriva un ejemplo de una mónada, y define formalmente "mónada".
Tenga en cuenta estas tres funciones en pseudocódigo:
f(<x, messages>) := <x, messages "called f. ">
g(<x, messages>) := <x, messages "called g. ">
wrap(x) := <x, "">
f
toma un par ordenado de la forma <x, messages>
y devuelve un par ordenado. Se deja el primer elemento sin tocar y anexa "called f. "
al segundo punto. Lo mismo con g
.
Puede componer estas funciones y obtener su valor original, junto con una cadena que muestra qué orden las funciones fueron llamados:
f(g(wrap(x)))
= f(g(<x, "">))
= f(<x, "called g. ">)
= <x, "called g. called f. ">
Que no le gusta el hecho de que f
y g
es responsable de sus propios añadiendo mensajes de registro de la información de registro anterior. (Imagínense por el bien del argumento que en lugar de cadenas anexas, f
y g
debe realizar la lógica complicada en el segundo elemento del par Sería un dolor de repetir que la lógica complicada en dos -. - o más. Distintas funciones)
Prefiere escribir funciones más simples:
f(x) := <x, "called f. ">
g(x) := <x, "called g. ">
wrap(x) := <x, "">
Pero mira lo que sucede cuando los componen:
f(g(wrap(x)))
= f(g(<x, "">))
= f(<<x, "">, "called g. ">)
= <<<x, "">, "called g. ">, "called f. ">
El problema es que pasar un par en una función no le da lo que quiere. Pero lo que si podría alimentar a una pareja en una función:
feed(f, feed(g, wrap(x)))
= feed(f, feed(g, <x, "">))
= feed(f, <x, "called g. ">)
= <x, "called g. called f. ">
Leer feed(f, m)
como "alimentar m
a f
". Para alimentar a una pareja <x, messages>
en una función f
es a pasar x
en f
, obtener <y, message>
fuera de f
, y volver <y, messages message>
.
feed(f, <x, messages>) := let <y, message> = f(x)
in <y, messages message>
Observe lo que sucede cuando haces tres cosas con sus funciones:
En primer lugar: si envuelves un valor y luego alimenta el par resultante en una función:
feed(f, wrap(x))
= feed(f, <x, "">)
= let <y, message> = f(x)
in <y, "" message>
= let <y, message> = <x, "called f. ">
in <y, "" message>
= <x, "" "called f. ">
= <x, "called f. ">
= f(x)
Ese es el mismo que el que pasa el valor en la función.
Segundo: si alimenta a un par en wrap
:
feed(wrap, <x, messages>)
= let <y, message> = wrap(x)
in <y, messages message>
= let <y, message> = <x, "">
in <y, messages message>
= <x, messages "">
= <x, messages>
Eso no cambia el par.
Tercero: si se define una función que toma x
y se alimenta g(x)
en f
:
h(x) := feed(f, g(x))
y alimentar un par en él:
feed(h, <x, messages>)
= let <y, message> = h(x)
in <y, messages message>
= let <y, message> = feed(f, g(x))
in <y, messages message>
= let <y, message> = feed(f, <x, "called g. ">)
in <y, messages message>
= let <y, message> = let <z, msg> = f(x)
in <z, "called g. " msg>
in <y, messages message>
= let <y, message> = let <z, msg> = <x, "called f. ">
in <z, "called g. " msg>
in <y, messages message>
= let <y, message> = <x, "called g. " "called f. ">
in <y, messages message>
= <x, messages "called g. " "called f. ">
= feed(f, <x, messages "called g. ">)
= feed(f, feed(g, <x, messages>))
Esa es la misma que la alimentación de la pareja en g
y alimentar el par resultante en f
.
Usted tiene más de una mónada. Ahora sólo tiene que saber acerca de los tipos de datos en su programa.
¿Qué tipo de valor es <x, "called f. ">
? Bueno, eso depende de qué tipo de valor x
es. Si x
es de tipo t
, entonces su pareja es un valor de tipo "par de t
y la cadena". Llamar a ese tipo M t
.
M
es un constructor de tipos: M
solo no se refiere a un tipo, pero M _
se refiere a un tipo una vez que llene el espacio con un tipo. Una M int
es un par de un int y una cadena. Una M string
es un par de una cadena y una cadena. Etc.
Enhorabuena, ha creado una monada!
Formalmente, su mónada es la tupla <M, feed, wrap>
.
Una mónada es una tupla <M, feed, wrap>
donde:
-
M
es un tipo constructor. -
feed
realiza un (función que toma unat
y devuelve unaM u
) y unaM t
y devuelve unM u
. -
wrap
toma unav
y devuelve unM v
.
t
, u
Y v
son los tres tipos que pueden o no pueden ser iguales. Una mónada satisface las tres propiedades que probaste para su mónada específica:
La alimentación de una envuelta
t
en una función es la misma que pasa el desempaquetadot
en la función.Formalmente:
feed(f, wrap(x)) = f(x)
La alimentación de un
M t
awrap
no hace nada para elM t
.Formalmente:
feed(wrap, m) = m
La alimentación de un
M t
(llamarm
) a una función que- pasa el
t
eng
- Obtiene un
M u
(llamarlon
) deg
- se alimenta
n
enf
es lo mismo que
- la alimentación
m
eng
- obtener
n
a partirg
- la alimentación
n
enf
Formalmente:
feed(h, m) = feed(f, feed(g, m))
dondeh(x) := feed(f, g(x))
- pasa el
Típicamente, feed
se llama bind
(AKA >>=
en Haskell) y wrap
se llama return
.
http://code.google.com/p/monad-tutorial/ es un trabajo en progreso para abordar precisamente esta cuestión.
Mónadas son para controlar el flujo de lo tipos abstractos de datos son los datos.
En otras palabras, muchos desarrolladores se sienten cómodos con la idea de conjuntos, listas, diccionarios (o hashes o asignaciones), y árboles. Dentro de estos tipos de datos, hay muchos casos especiales (por ejemplo InsertionOrderPreservingIdentityHashMap).
Sin embargo, cuando se enfrentan con "flujo" programa de muchos desarrolladores no han sido expuestos a muchas más construcciones que si, switch / case, hacer, mientras que, Goto (GRR), y (tal vez) cierres.
Por lo tanto, una mónada no es más que una construcción de control de flujo. Una mejor frase para reemplazar 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 retirarla.
Por ejemplo, el "si" mónada:
if( clause ) then block
En su más simple tiene dos ranuras - una cláusula, y un bloque. La if
mónada se construye generalmente para evaluar el resultado de la cláusula, y si no es falso, evaluar el bloque. Muchos desarrolladores no se introducen a mónadas cuando se enteran ''si'', y simplemente no son necesarios para comprender las mónadas para escribir la lógica eficaz.
Mónadas pueden llegar a ser más complicado, de la misma manera que las estructuras de datos pueden llegar a ser más complicado, pero hay muchas categorías amplias de mónada que puede tener una semántica similar, pero que difieren implementaciones y sintaxis.
Por supuesto, de la misma manera que las estructuras de datos pueden ser repiten a lo largo, o atravesados, mónadas pueden ser evaluados.
Los compiladores pueden o no tener soporte para mónadas definidos por el usuario. Haskell sin duda lo hace. Ioke tiene algunas capacidades similares, aunque el término mónada no se utiliza en el lenguaje.
Una mónada es una cosa utilizado para encapsular objetos que han estado cambiante. Con mayor frecuencia se encuentra en los idiomas que de otro modo no le permiten tener estado modificables (por ejemplo, Haskell).
Un ejemplo podría ser para el archivo de E / S.
Usted sería capaz de utilizar una mónada de archivo de E / S para aislar la naturaleza cambiante estado de sólo el código que utiliza la mónada. El código dentro de la mónada puede ignorar efectivamente el estado cambiante del mundo fuera de la Mónada - esto hace que sea mucho más fácil de razonar acerca de los efectos generales de su programa.
Una mónada es, efectivamente, una forma de "operador tipo". Se va a hacer tres cosas. En primer lugar, se "wrap" (o de otra manera convertir) un valor de un tipo a otro tipo (normalmente denominado un "tipo monádico"). En segundo lugar que hará que todas las operaciones (o funciones) disponibles en el tipo subyacente disponible en el tipo monádico. Finalmente se proporcionará apoyo para la combinación de su auto con otra mónada para producir una mónada compuesto.
El "tal vez mónada" es esencialmente el equivalente de "tipos anulables" en Visual Basic / C #. Se necesita un tipo no anulable "T" y la convierte en un "Anulable <T>", y luego define lo que significan todos los operadores binarios en un Anulable <T>.
Los efectos secundarios son representados simillarly. Una estructura se crea que contiene descripciones de los efectos secundarios, junto con el valor devuelto por una función. Las operaciones de "levantadas", entonces la copia alrededor de efectos secundarios como valores se pasan entre las funciones.
Se les llama "mónadas" más que el nombre más fácil de comprensión de los operadores "tipo" por varias razones:
- Mónadas tienen restricciones en lo que pueden hacer (ver el definiton para más detalles).
- Esas restricciones, junto con el hecho de que hay tres operaciones involucradas, se ajustan a la estructura de algo que se llama una mónada en la teoría de categorías, que es una oscura rama de las matemáticas.
- Fueron diseñados por los defensores de los lenguajes funcionales "puros"
- Los defensores de los lenguajes funcionales puros como ramas oscuras de las matemáticas
- Debido a que la matemática es oscura, y las mónadas están asociados con estilos particulares de programación, la gente tiende a usar la palabra mónada como una especie de apretón de manos secreto. Debido a esto no se ha molestado en invertir en un nombre mejor.
Todavía soy nuevo en mónadas, pero pensé que iba a compartir un enlace que encontré que se sentía muy bueno para leer (con fotos !!): http://www.matusiak.eu/numerodix/blog/2012/3/11/ mónadas-para-el-laico / (sin afiliación)
Básicamente, el concepto cálida y difusa que obtuve de que el artículo era el concepto de que las mónadas son, básicamente, los adaptadores que permiten funciones dispares para trabajar de una manera componibles, es decir, ser capaz de colgar a múltiples funciones y mezclar y combinar sin preocuparse de retorno inconsistente tipos y tal. Por lo tanto la función BIND es el encargado de mantener las manzanas con manzanas y naranjas con naranjas cuando estamos tratando de hacer estos adaptadores. Y la función de elevación es el encargado de tomar las funciones de "nivel inferior" y "modernización" que les permite trabajar con funciones de BIND y ser componibles también.
Espero que lo hizo bien, y lo más importante, la esperanza de que el artículo tiene una visión válida en mónadas. Por lo menos, este artículo ayudó a ir abriendo el apetito para aprender más acerca de las mónadas.