haskell - monadologia - monadas filosofia
Función de unión de mónada (5)
De hecho, en cierto modo, join
es donde realmente sucede toda la magia-- (>>=)
se usa principalmente para su conveniencia.
Todas las clases de tipos basadas en Functor
describen una estructura adicional utilizando algún tipo. Con Functor
esta estructura adicional a menudo se la considera un "contenedor", mientras que con Monad
tiende a considerarse como "efectos secundarios", pero esos son solo taquigrafías (a veces engañosas), es lo mismo de cualquier manera y no realmente cualquier cosa especial [0] .
La característica distintiva de Monad
comparación con otras Functor
es que puede integrar el flujo de control en la estructura adicional. La razón por la que puede hacer esto es que, a diferencia de fmap
que aplica una función plana única sobre toda la estructura, (>>=)
inspecciona elementos individuales y construye una nueva estructura a partir de eso.
Con un Functor
plano, la construcción de una nueva estructura a partir de cada parte de la estructura original Functor
al Functor
, y cada capa representa un punto de flujo de control. Esto obviamente limita la utilidad, ya que el resultado es desordenado y tiene un tipo que refleja la estructura del control de flujo utilizado.
Los "efectos secundarios" monádicos son estructuras que tienen algunas propiedades adicionales [1] :
- Dos efectos secundarios se pueden agrupar en uno (por ejemplo, "do X" y "do Y" se convierten en "do X, luego Y"), y el orden de agrupación no importa mientras se mantenga el orden de los efectos.
- Existe un efecto secundario de "no hacer nada" (por ejemplo, "hacer X" y "no hacer nada" agrupados es lo mismo que "hacer X")
La función de join
no es más que esa operación de agrupación: un tipo de mónada anidada como m (ma)
describe dos efectos secundarios y el orden en que aparecen, y la join
los agrupa en un solo efecto secundario.
Por lo tanto, en lo que respecta a los efectos secundarios monádicos, la operación de enlace es una abreviatura de "tomar un valor con efectos secundarios asociados y una función que introduce nuevos efectos secundarios, luego aplicar la función al valor mientras se combinan los efectos secundarios para cada uno" .
[0]: Excepto IO
. IO
es muy especial.
[1]: Si comparas estas propiedades con las reglas de una instancia de Monoid
, verás un paralelismo cercano entre las dos. Esto no es una coincidencia, y de hecho es lo que "es solo un monoide en la categoría de endofunctores. ¿Cuál es el problema?" Se refiere a la línea.
Si bien las mónadas se representan en Haskell con las funciones de enlace y retorno, también pueden tener otra representación con la función de unión, como se explica aquí . Sé que el tipo de esta función es M (M (X)) -> M (X), pero ¿qué hace esto en realidad?
Desde la misma página recuperamos esta información join x = x >>= id
, con el conocimiento de cómo funcionan las funciones de bind
e id
, debería ser capaz de averiguar qué hace la join
.
Lo que hace la unión ha sido adecuadamente descrito por las otras respuestas hasta ahora, creo. Si está buscando una comprensión más intuitiva ... si se está preguntando qué significa "unión" ... entonces, desafortunadamente, la respuesta variará dependiendo de la mónada en cuestión, específicamente de lo que significa M (X) " "y lo que M (M (X))" significa ".
Si M es la mónada de la Lista, entonces M (M (X)) es una lista de listas, y unirse significa "aplanar". Si M es la mónada Maybe, entonces un elemento de M (M (X)) podría ser "Just (Just x)", "Just Nothing", o "Nothing", y unirse significa colapsar esas estructuras de forma lógica para "Solo x", "Nada" y "Nada" respectivamente (similar a la respuesta de camccann de combinar como efectos secundarios).
Para las mónadas más complicadas, M (M (X)) se convierte en algo muy abstracto y decidir qué M (M (X)) y unir "media" se vuelve más complicado. En todos los casos es algo así como el caso de la mónada de la Lista, en el que colapsas dos capas de abstracción de la Mónada en una capa, pero el significado va a variar. Para la mónada Estatal, la respuesta de camccann de combinar dos efectos secundarios es: unir significa esencialmente combinar dos transiciones de estado sucesivas. La mónada de Continuación es especialmente innovadora, pero la combinación matemática en realidad es bastante clara aquí: M (X) corresponde al "doble espacio doble" de X, lo que los matemáticos podrían escribir como X**
(continuaciones en sí mismas, es decir, mapas de X- > R donde R es un conjunto de resultados finales, corresponde al único espacio dual X*
), y la combinación corresponde a un mapa extremadamente natural de X****
a X**
. El hecho de que las mónadas de Continuación cumplan con las leyes de la mónada corresponde al hecho matemático de que generalmente no tiene mucho sentido aplicar el operador de espacio dual *
más de dos veces.
Pero yo divago.
Personalmente trato de resistir la tentación de aplicar una analogía única a todos los tipos posibles de mónadas; las mónadas son un concepto demasiado general para ser encasillado por una única analogía descriptiva. Lo que significa unirse va a variar según la analogía con la que estés trabajando en un momento dado.
Lo que hace, conceptualmente, se puede determinar con solo mirar el tipo: desenvuelve o aplana el contenedor / cálculo monádico exterior y devuelve el (los) valor (es) monádico (s) producido (s) en el mismo.
La forma en que lo hace realmente está determinada por el tipo de mónada con la que está tratando. Por ejemplo, para la mónada de la Lista, ''unirse'' es equivalente a concat .
Los mapas de la operación de enlace: ma -> (a -> mb) -> mb
. En ma
y (la primera) mb
, tenemos dos m
s. Para mi intuición, la comprensión de las operaciones vinculantes y monádicas ha llegado a mentir, en gran medida, en la comprensión de cómo y de qué manera se combinarán esos dos m
s (instancias del contexto monádico). Me gusta pensar en la mónada del escritor como un ejemplo para comprender la join
. Writer se puede utilizar para registrar operaciones. ma
tiene un log en ella (a -> mb)
producirá otro registro en ese primer mb
. El segundo mb
combina ambos registros.
(Y un mal ejemplo es pensar en la mónada Tal vez, porque Just
+ Just
= Just
and Nothing
+ anything = Nothing
(o F # Some
and None
) son tan poco informativos que se pasa por alto el hecho de que algo importante está sucediendo. pensar simplemente como una única condición para proceder y Nothing
como simplemente una sola bandera para detener. Como señales en el camino, dejadas atrás a medida que avanza el cálculo (lo que es una impresión razonable, ya que parece que no se ha creado el Just
o Nothing
final. arañe el último paso de la computación sin que se transfiera nada de los anteriores. Cuando realmente necesite enfocarse en la combinatoria de Just
sy Nothing
s en cada ocasión.)
El tema se cristalizó para mí al leer el artículo de Miran Lipovaca, ¡Aprendan un haskell para el bien !, Capítulo 12, la última sección sobre las leyes de la mónada. http://learnyouahaskell.com/a-fistful-of-monads#monad-laws , Associativity. Este requisito es: "Hacer (ma >>= f) >>= g
es como hacer ma >>= (/x -> fx >>= g)
[uso ma
para m
]". Bueno, en ambos lados el argumento pasa primero a f
luego a g
. Entonces, ¿qué quiere decir con "no es fácil ver cómo esos dos son iguales"? ¡No es fácil ver cómo pueden ser diferentes!
La diferencia está en la asociatividad de las uniones de m
s (contextos), lo que hacen los enlaces, junto con el mapeo. La vinculación se desenrolla o gira alrededor de la m
para llegar a la a la que se aplica f
, pero eso no es todo. La primera m
(en ma
) se mantiene mientras que f
genera una segunda m
(en mb
). Luego bind
combina-- join
s - ambos m
s. La clave para bind
está tanto en la join
como en el desenvolvimiento ( map
). Y creo que la confusión sobre la join
es indicativa de fijarse en el aspecto de desenvolvimiento de bind
obtener la a
out of ma
para coincidir con la firma del argumento de f
- y pasar por alto el hecho de que los dos m
s (de ma
y entonces mb
) hay que reconciliarse. (Descartar la primera m
puede ser la forma adecuada de manejarlo en algunos casos (quizás), pero eso no es cierto en general, como lo ilustra Writer).
A la izquierda, unimos ma
a f
primero, luego a g
segundo. Así que el registro será como: ("before f" + "after f") + "after g"
. A la derecha, mientras que las funciones f
y g
se aplican en el mismo orden, ahora enlazamos primero con g
. Así que el registro será como: "before f" + ("after f" + "after g")
. Los parens no están en la (s) cadena (s), por lo que el registro es el mismo en ambos sentidos y se cumple la ley. (Considerando que si el segundo registro hubiera salido como "after f" + "after g" + "before f"
- ¡entonces estaríamos en problemas matemáticos!).
Si bind
fmap
bind
como fmap
más join
para Writer, obtenemos fmap f ma
, donde f:a -> mb
, lo que da como resultado m(mb)
. Piense en la primera m
en ma
como "antes de f". La f
se aplica a la a
dentro de esa primera m
ahora llega una segunda m
(o mb
), dentro de la primera m
, donde se realiza el mapeo f
. Piense en la segunda m
en mb
como "después de f". m(mb)
= ("antes de f" ("después de f" b
)). Ahora usamos Unir para colapsar los dos registros, el m
s, haciendo un nuevo m
. Escritor utiliza un monoide y concatenamos. Otras mónadas combinan contextos de otras maneras, obedeciendo las leyes. Que tal vez sea la parte principal de entenderlos.