haskell - traduccion - monads scala
¿Cómo identifica los patrones de diseño monádico? (4)
Mi manera de aprender Haskell Empiezo a comprender el concepto de la mónada y comenzar a usar las mónadas conocidas en mi código, pero todavía tengo dificultades para acercarme a las mónadas desde el punto de vista del diseñador. En OO hay varias reglas como, "identificar sustantivos" para objetos, observar algún tipo de estado e interfaz ... pero no puedo encontrar recursos equivalentes para mónadas.
Entonces, ¿cómo identificas un problema como de naturaleza monádica? ¿Cuáles son los buenos patrones de diseño para el diseño monádico? ¿Cuál es su enfoque cuando se da cuenta de que algún código sería mejor refactorizado en una mónada?
Está la vista de efecto de las mónadas:
- Tal vez - cortocircuito de parcialidad / falla
- O bien: informe de error / cortocircuito (como Maybe con más información)
- Escritor: escriba solo "estado", comúnmente logging
- Lector: estado de solo lectura, comúnmente pasa el entorno
- Estado - estado de lectura / escritura
- Reanudación: cálculo pausable
- Lista - éxitos múltiples
Una vez que esté familiarizado con estos efectos, es fácil construir mónadas combinándolos con transformadores de mónada. Tenga en cuenta que la combinación de algunas mónadas necesita cuidados especiales (particularmente Cont y cualquier mónada con retroceso).
Una cosa importante a tener en cuenta es que no hay muchas mónadas. Hay algunos exóticos que no están en las bibliotecas estándar, por ejemplo, la mónada de probabilidad y las variaciones de la mónada Cont como Codensity. Pero a menos que esté haciendo algo matemático, es poco probable que invente (o descubra) una nueva mónada, sin embargo, si usa Haskell el tiempo suficiente, construirá muchas mónadas que son combinaciones diferentes de las estándar.
Editar: también tenga en cuenta que el orden en que se acumulan los transformadores de mónada genera diferentes mónadas:
Si agrega ErrorT (transformador) a una mónada Writer, obtiene esta mónada. Either err (log,a)
: solo puede acceder al registro si no tiene ningún error.
Si agrega WriterT (transfomer) a una mónada Error, obtiene esta mónada (log, Either err a)
que siempre da acceso al registro.
Esto es una especie de falta de respuesta, pero creo que es importante decirlo de todos modos. ¡Solo pregunta! , / r / haskell, y el canal #haskell irc son todos excelentes lugares para recibir comentarios rápidos de personas inteligentes. Si estás trabajando en un problema y sospechas que hay algo de magia monádica que podría facilitarlo, ¡solo pregúntaselo! La comunidad Haskell ama resolver problemas y es ridículamente amigable.
No malinterpretes, no te animo a que nunca aprendas por ti mismo. Por el contrario, interactuar con la comunidad de Haskell es una de las mejores formas de aprender. LYAH y RWH , 2 libros de Haskell que están disponibles gratuitamente en línea, también son muy recomendables.
Ah, y no te olvides de jugar, jugar, jugar! A medida que juega con el código monádico, comenzará a tener la sensación de lo que tienen las mónadas "forma", y cuando los combinadores monádicos pueden ser útiles. Si está moviendo su propia mónada, generalmente el sistema de tipos lo guiará a una solución obvia y simple. Pero para ser honesto, rara vez debería necesitar rodar su propia instancia de Monad, ya que las bibliotecas de Haskell proporcionan toneladas de cosas útiles, tal como lo mencionaron otros encuestados.
Sigue los tipos.
Si encuentra que tiene funciones escritas con todos estos tipos
-
(a -> b) -> YourType a -> YourType b
-
a -> YourType a
-
YourType (YourType a) -> YourType a
o todos estos tipos
-
a -> YourType a
-
YourType a -> (a -> YourType b) -> YourType b
entonces YourType
puede ser una mónada. (Digo "puede" porque las funciones también deben obedecer las leyes de la mónada).
(Recuerde que puede reordenar argumentos, por ejemplo, YourType a -> (a -> b) -> YourType b
es justo (a -> b) -> YourType a -> YourType b
disfrazado).
¡No mires solo por mónadas! Si tienes funciones de todos estos tipos
-
YourType
-
YourType -> YourType -> YourType
y obedecen las leyes de monos, ¡tienes un monoide! Eso también puede ser valioso. Del mismo modo para otras clases de tipos, lo más importante es Functor.
Una regla general útil es cuando ve valores en un contexto ; las mónadas se pueden ver como "efectos" de capas en:
- Tal vez: parcialidad (usos: cálculos que pueden fallar)
- O bien: errores de cortocircuito (usos: manejo de errores / excepciones)
- [] (la lista de mónadas): no determinismo (usos: generación de listas, filtrado, ...)
- Estado: una sola referencia mutable (utiliza: estado)
- Lector: un entorno compartido (usos: enlaces de variables, información común, ...)
- Writer: una salida o acumulación de "canal lateral" (usos: registro, mantenimiento de un contador de solo escritura, ...)
- Cont: flujo de control no local (usos: demasiado numerosos para listar)
Por lo general, generalmente debe diseñar su mónada colocando capas en los transformadores de mónada de la Biblioteca de transformadores de mónadas estándar, que le permiten combinar los efectos anteriores en una sola mónada. Juntos, estos manejan la mayoría de las mónadas que podría querer usar. Hay algunas mónadas adicionales no incluidas en el MTL, como la probability y supply mónadas de supply .
En cuanto a desarrollar una intuición sobre si un tipo definido recientemente es una mónada y cómo se comporta como uno solo, puedes pensar en ello yendo desde Functor
a Monad
:
- Functor le permite transformar valores con funciones puras.
- Aplicativo le permite incrustar valores puros y la aplicación expresa -
(<*>)
permite pasar de una función incrustada y su argumento incrustado a un resultado incrustado. - Monad permite que la estructura de los cálculos integrados dependa de los valores de cálculos previos.
La forma más fácil de entender esto es observar el tipo de join
:
join :: (Monad m) => m (m a) -> m a
Esto significa que si tiene un cálculo incrustado cuyo resultado es un nuevo cálculo incrustado, puede crear un cálculo que ejecute el resultado de ese cálculo. De modo que puede usar efectos monádicos para crear un nuevo cálculo basado en valores de cómputos previos, y transferir el flujo de control a ese cálculo.
Curiosamente, esto puede ser una debilidad de estructurar cosas monádicamente: con Applicative
, la estructura del cálculo es estática (es decir, un cálculo Applicative
dado tiene una cierta estructura de efectos que no puede cambiar en base a valores intermedios), mientras que con Monad
es dinámico. Esto puede restringir la optimización que puede hacer; por ejemplo, los analizadores sintácticos son menos poderosos que los monádicos (bueno, esto no es estrictamente cierto , pero lo es efectivamente), pero se pueden optimizar mejor.
Tenga en cuenta que (>>=)
se puede definir como
m >>= f = join (fmap f m)
y así una mónada puede definirse simplemente con return
y join
(asumiendo que es un Functor
, todas las mónadas son funcionadoras aplicativas, pero la jerarquía de tipos de Haskell desafortunadamente no requiere esto por razones históricas ).
Como nota adicional, probablemente no deberías concentrarte demasiado en las mónadas, sin importar qué tipo de zumbido reciban de los no Haskellers equivocados. Hay muchas clases de tipos que representan patrones significativos y potentes, y no todo se expresa mejor como una mónada. Applicative , Monoid , Foldable ... cuya abstracción depende completamente de su situación. Y, por supuesto, el hecho de que algo sea una mónada no significa que no pueda ser otras cosas también; ser una mónada es solo otra propiedad de un tipo.
Entonces, no debes pensar demasiado sobre "identificar mónadas"; las preguntas son más como:
- ¿Puede este código expresarse en una forma monádica más simple? ¿Con qué mónada?
- ¿Este tipo acabo de definir una mónada? ¿Qué patrones genéricos codificados por las funciones estándar en mónadas puedo aprovechar?