what traduccion programacion monada monad maplestory ergo haskell functional-programming monads

haskell - traduccion - ¿Son las "operaciones de encadenamiento" lo "único" que resuelve la clase Monad?



monads scala (2)

Definitivamente tienes algo en la manera en que lo estás diciendo: hay muchas cosas que significa Monad y las has separado bien.

Dicho esto, definitivamente diría que las operaciones de encadenamiento no son lo principal resuelto por las Mónadas. Eso se puede resolver utilizando Functors planos (con algunos problemas) o fácilmente con los aplicativos. Debe utilizar la especificación de la mónada completa cuando se "encadena con la opción". En particular, la tensión entre el Applicative y la Monad proviene del Applicative necesita conocer la estructura completa del cálculo de efectos secundarios de forma estática. Monad puede cambiar esa estructura en tiempo de ejecución y, por lo tanto, sacrifica cierta capacidad de análisis para obtener potencia.

Para aclarar el punto, el único lugar en el que tratas con una mónada pero no con una mónada específica es si estás definiendo algo con polimorfismo limitado a ser una mónada . Esto aparece repetidamente en el módulo Control.Monad , por lo que podemos examinar algunos ejemplos a partir de ahí.

sequence :: [m a] -> m [a] forever :: m a -> m b foldM :: (a -> b -> m a) -> a -> [b] -> m a

Inmediatamente, podemos descartar una sequence como particular de la Monad ya que hay una función correspondiente en la sequenceA Data.Traversable , que tiene un tipo ligeramente más general que el Applicative f => [fa] -> f [a] . Esto debería ser un claro indicador de que Monad no es la única forma de secuenciar las cosas .

Del mismo modo, podemos definir para foreverA lo siguiente.

foreverA :: Applicative f => f a -> f b foreverA f = flip const <$> f <*> foreverA f

Así que hay más formas de secuenciar tipos no- Monad . Pero nos encontramos con problemas con foldM

foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a foldM _ a [] = return a foldM f a (x:xs) = f a x >>= /fax -> foldM f fax xs

Si intentamos traducir esta definición al estilo Applicative podríamos escribir

foldA :: (Applicative f) => (a -> b -> f a) -> a -> [b] -> f a foldA _ a [] = pure a foldA f a (x:xs) = foldA f <$> f a x <*> xs

Pero Haskell se quejará con razón de que esto no se verifica: cada llamada recursiva a foldA intenta poner otra "capa" de f en el resultado. Con Monad podríamos join esas capas hacia abajo, pero el Applicative es demasiado débil.

Entonces, ¿cómo se traduce esto a los Applicative nos restringen las opciones de tiempo de ejecución? Bueno, eso es exactamente lo que expresamos con foldM , un cálculo monádico (a -> b -> ma) que depende de su argumento, un resultado de un cálculo monádico anterior. Ese tipo de cosas simplemente no tiene ningún significado en el mundo más puramente secuencial de Applicative .

Para aclarar la pregunta: se trata de los méritos de la clase de tipo mónada (a diferencia de sus instancias sin la clase unificadora).

Después de haber leído muchas referencias (ver más abajo), llegué a la conclusión de que, en realidad, la clase mónada está ahí para resolver un solo problema, pero grande y crucial : el "encadenamiento" de funciones en tipos con contexto . Por lo tanto, la famosa frase "las mónadas son puntos y coma programables". De hecho, una mónada puede verse como una serie de funciones con operaciones de ayuda .

Insisto en la diferencia entre la clase de mónada, entendida como una interfaz general para otros tipos; y estos otros tipos que instancian la clase (por lo tanto, "tipos monádicos").

Entiendo que la clase de mónada por sí sola, solo resuelve el encadenamiento de operadores porque principalmente, solo obliga a sus instancias de tipo a tener bind >>= y return , y nos dice cómo deben comportarse. Y como beneficio adicional, el compilador ayuda mucho a la codificación, proporcionando notación para tipos monádicos.

Por otro lado, cada tipo individual de instanciación de la clase de mónada es el que resuelve cada problema concreto , pero no solo por ser una instancia de Mónada . Por ejemplo, Maybe resuelve "cómo una función devuelve un valor o un error", el State resuelve "cómo tener funciones con estado global", IO resuelve "cómo interactuar con el mundo exterior", etc. Todas las clases de tesis encapsulan un valor dentro de un contexto.

Pero pronto o más tarde, necesitaremos encadenar operaciones en tales tipos de contexto. Es decir, necesitaremos organizar llamadas a funciones en estos tipos en una secuencia particular (para un ejemplo de tal problema, lea el ejemplo sobre funciones multivalor en Usted podría haber inventado las mónadas ).

Y puede resolver el problema del encadenamiento, si tiene cada tipo como una instancia de la clase mónada. Para que el encadenamiento funcione, necesita >>= solo con la firma exacta que tiene, ninguna otra. (Ver esta pregunta ).

Por lo tanto, supongo que la próxima vez que defina un tipo de datos de contexto T para resolver algo, si necesita secuenciar llamadas de funciones (en los valores de T) considere hacer de T una instancia de Monad (si necesita "encadenar con elección" y Si puede beneficiarse de la notación de do ). Y para asegurarse de que lo está haciendo bien, verifique que T cumple con las leyes de la mónada

Luego, hago dos preguntas a los expertos de Haskell:

  1. Una pregunta concreta: ¿hay algún otro problema que la clase de mónada resuelva por sí mismo (dejando de lado las clases monádicas)? Si hay alguno, entonces, ¿cómo se compara en relevancia con el problema de las operaciones de encadenamiento?
  2. Una pregunta general opcional: ¿son correctas mis conclusiones, estoy malinterpretando algo?

Referencias

Tutoriales

Preguntas y respuestas sobre StackOverflow


Para resolver el problema de encadenar las operaciones en un tipo monádico individual, no es en absoluto necesario convertirlo en una instancia de la Monad y asegurarse de que se cumplan las leyes de la mónada. Simplemente podría implementar una operación de encadenamiento directamente en su tipo.

Probablemente sería muy similar al enlace monádico, pero no necesariamente exactamente el mismo (recordar que el enlace para las listas es concatMap , una función que existe de todos modos, pero con los argumentos en un orden diferente). Y no tendría que preocuparse por las leyes de la mónada, porque tendría una interfaz ligeramente diferente para cada tipo, por lo que no tendrían requisitos comunes.

Para preguntar qué problema resuelve la clase de tipo Monad, mire todas las funciones (en Control.Monad y dónde más) que funcionan con valores en cualquier tipo monádico. El problema resuelto es la reutilización de código! Monad es exactamente la parte de todos los tipos monádicos que es común a todos y cada uno de ellos. Esa parte es suficiente por sí sola para escribir cálculos útiles. Todas estas funciones podrían implementarse para cualquier tipo monádico individual (a menudo más directamente), pero ya se han implementado para todos los tipos monádicos, incluso los que aún no existen.

No escribe una instancia de Monad para poder encadenar operaciones en su tipo (de hecho, a menudo ya tiene una forma de encadenar). Escribe una instancia de Monad para todo el código que viene automáticamente junto con la instancia de Monad . Monad no se trata de resolver ningún problema para un solo tipo, es una forma de ver muchos tipos dispares como ejemplos de un solo concepto unificador.