mónada monadas leibniz las filosofía filosofia educatina doctrina haskell monads

haskell - leibniz - monadas filosofia



Haskell Mónada confundir operador confusión (3)

El artículo que vincula se basa en el artículo de sigfpe, que utiliza una definición de enlace invertida:

Lo primero es que he cambiado la definición de bind y la he escrito como la palabra ''vincular'', mientras que normalmente se escribe como el operador >>= . Así que bind fx normalmente se escribe como x >>= f .

Entonces, el bind Haskell toma un valor encerrado en una mónada y devuelve una función, que toma una función y luego la llama con el valor extraído. Podría estar usando una terminología no precisa, así que quizás sea mejor con el código.

Tienes:

sine x = (sin x, "sine was called.") cube x = (x * x * x, "cube was called.")

Ahora, la conversión de su enlace JS (Haskell hace curry automático, por lo que llamar bind f devuelve una función que toma una tupla, y luego la coincidencia de patrones se encarga de desempaquetarlo en x y s , espero que sea comprensible):

bind f (x, s) = (y, s ++ t) where (y, t) = f x

Puedes verlo funcionando:

*Main> :t sine sine :: Floating t => t -> (t, [Char]) *Main> :t bind sine bind sine :: Floating t1 => (t1, [Char]) -> (t1, [Char]) *Main> (bind sine . bind cube) (3, "") (0.956375928404503,"cube was called.sine was called.")

Ahora, invirtamos los argumentos de bind :

bind'' (x, s) f = (y, s ++ t) where (y, t) = f x

Se puede ver claramente que sigue haciendo lo mismo, pero con una sintaxis un poco diferente:

*Main> bind'' (bind'' (3, "") cube) sine (0.956375928404503,"cube was called.sine was called.")

Ahora, Haskell tiene un truco de sintaxis que le permite usar cualquier función como operador de infijo. Así que puedes escribir:

*Main> (3, "") `bind''` cube `bind''` sine (0.956375928404503,"cube was called.sine was called.")

Ahora cambie el nombre de bind'' a >>= ( (3, "") >>= cube >>= sine ) y tendrá lo que estaba buscando. Como puede ver, con esta definición, puede deshacerse efectivamente del operador de composición separado.

Volver a traducir lo nuevo a JavaScript generaría algo como esto (note que, de nuevo, solo invierto el orden de los argumentos):

var bind = function(tuple) { return function(f) { var x = tuple[0], s = tuple[1], fx = f(x), y = fx[0], t = fx[1]; return [y, s + t]; }; }; // ugly, but it''s JS, after all var f = function(x) { return bind(bind(x)(cube))(sine); } f([3, ""]); // [0.956375928404503, "cube was called.sine was called."]

Espero que esto ayude, y no introduzca más confusión, el punto es que esas dos definiciones de enlace son equivalentes, solo que difieren en la sintaxis de llamadas.

Bien, entonces no soy un programador de Haskell, pero estoy absolutamente intrigado por muchas de las ideas detrás de Haskell y estoy pensando en aprenderlo. Pero estoy atascado en el punto uno: parece que no puedo envolver mi cabeza alrededor de las mónadas, que parecen ser bastante fundamentales. Sé que hay un millón de preguntas sobre SO que piden explicar las Mónadas, así que voy a ser un poco más específico sobre lo que me está molestando:

Leí este excelente artículo ( una introducción en Javascript ), y pensé que entendía Mónadas completamente. Luego leí la entrada de Wikipedia en Monads, y vi esto:

Una operación de unión de tipo polimórfico (M t) → (t → M u) → (M u), que Haskell representa por el operador de infijo >> =. Su primer argumento es un valor en un tipo monádico, su segundo argumento es una función que se asigna del tipo subyacente del primer argumento a otro tipo monádico, y su resultado está en ese otro tipo monádico.

De acuerdo, en el artículo que cité, bind era una función que tomaba solo un argumento. Wikipedia dice dos. Lo que pensé que entendí sobre Monads fue lo siguiente:

  1. El propósito de una Mónada es tomar una función con diferentes tipos de entrada y salida y hacerla compostable. Lo hace envolviendo los tipos de entrada y salida con un solo tipo monádico.
  2. Una mónada consta de dos funciones interrelacionadas: vinculación y unidad. Bind toma una función no composible f y devuelve una nueva función g que acepta el tipo monádico como entrada y devuelve el tipo monádico. g es composable. La función de unidad toma un argumento del tipo que f esperaba y lo envuelve en el tipo monádico. Esto se puede pasar a g, o a cualquier composición de funciones como g.

Pero debe haber algo mal, porque mi concepto de vinculación toma un argumento: una función. ¡Pero (de acuerdo con Wikipedia) el vínculo de Haskell en realidad toma dos argumentos! ¿Dónde está mi error?


No estás cometiendo un error. La idea clave a entender aquí es el curry, que una función de Haskell de dos argumentos se puede ver de dos maneras. El primero es simplemente una función de dos argumentos. Si tiene, por ejemplo, (+) , esto generalmente se considera como tomar dos argumentos y agregarlos. La otra forma de verlo es como productor adicional de máquinas. (+) es una función que toma un número, digamos x , y hace una función que sumará x .

(+) x = /y -> x + y (+) x y = (/y -> x + y) y = x + y

Cuando se trata de mónadas, a veces es mejor, como lo mencionado anteriormente, pensar en =<< , la versión invertida de >>= . Hay dos maneras de ver esto:

(=<<) :: (a -> m b) -> m a -> m b

que es una función de dos argumentos, y

(=<<) :: (a -> m b) -> (m a -> m b)

que transforma la función de entrada en una versión fácilmente compuesta como el artículo mencionado. Estos son equivalentes como (+) como expliqué antes.


Permíteme derribar tus creencias sobre las mónadas. Espero sinceramente que te des cuenta de que no estoy tratando de ser grosero; Simplemente estoy tratando de evitar las palabras molestas.

El propósito de una Mónada es tomar una función con diferentes tipos de entrada y salida y hacer que sea compostable. Lo hace envolviendo los tipos de entrada y salida con un solo tipo monádico.

No exactamente. Cuando comienzas una oración con "el propósito de una mónada", ya estás en el pie equivocado. Las mónadas no necesariamente tienen un "propósito". Monad es simplemente una abstracción, una clasificación que se aplica a ciertos tipos y no a otros. El propósito de la abstracción de la Monad es simplemente eso, abstracción.

Una mónada consta de dos funciones interrelacionadas: vinculación y unidad.

Si y no. La combinación de bind y unit son suficientes para definir una Mónada, pero la combinación de join , fmap y unit son igualmente suficientes. La última es, de hecho, la forma en que las mónadas se describen normalmente en la teoría de categorías.

Bind toma una función no composible f y devuelve una nueva función g que acepta el tipo monádico como entrada y devuelve el tipo monádico.

Una vez más, no exactamente. Una función monádica f :: a -> mb es perfectamente componible, con ciertos tipos. Puedo post-componerlo con una función g :: mb -> c para obtener g . f :: a -> c g . f :: a -> c , o puedo pre-componerlo con una función h :: c -> a para obtener f . h :: c -> mb f . h :: c -> mb .

Pero tienes la segunda parte absolutamente correcta: (>>= f) :: ma -> mb . Como otros han señalado, la función de bind de Haskell toma los argumentos en el orden opuesto.

g es composable.

Bueno, sí. Si g :: ma -> mb , entonces puedes pre-componerlo con una función f :: c -> ma para obtener g . f :: c -> mb g . f :: c -> mb , o puede post-componerlo con una función h :: mb -> c para obtener h . g :: ma -> c h . g :: ma -> c . Tenga en cuenta que c podría tener la forma mv donde m es una mónada. Supongo que cuando dices "compostable" quieres decir "puedes componer cadenas de funciones arbitrariamente largas de esta forma", lo cual es bastante cierto.

La función de unidad toma un argumento del tipo que f esperaba y lo envuelve en el tipo monádico.

Una forma indirecta de decirlo, pero sí, eso es correcto.

Este [el resultado de aplicar la unit a algún valor] puede luego pasar a g, o a cualquier composición de funciones como g.

Una vez más, sí. Aunque generalmente no es idiomático Haskell llamar unit (o en Haskell, return ) y luego pasar eso a (>>= f) .

-- instead of return x >>= f >>= g -- simply go with f x >>= g -- instead of /x -> return x >>= f >>= g -- simply go with f >=> g -- or g <=< f