you monad learn haskell binding

haskell - monad - “<-” enlaces en notación do



list monad (6)

Me cuesta mucho entender esto. Al escribir en notación do, ¿en qué se diferencian las siguientes dos líneas?

1. let x = expression 2. x <- expression

No puedo verlo A veces uno funciona, otras veces el otro. Pero rara vez ambos. "Learn you a haskell" dice que <- une el lado derecho al símbolo de la izquierda. Pero, ¿en qué se diferencia eso de simplemente definir x con let ?


Aquí hay un ejemplo simple que le muestra la diferencia. Considere las dos expresiones simples siguientes:

letExpression = 2 bindExpression = Just 2

La información que está intentando recuperar es el número 2 . Así es como lo haces:

let x = letExpression x <- bindExpression

let poner directamente el valor 2 en x . <- extrae el valor 2 del Just y lo pone en x .

Puede ver con ese ejemplo, por qué estas dos notaciones no son intercambiables:

let x = bindExpression pondría directamente el valor Just 2 en x . x <- letExpression no tendría nada que extraer y poner en x .


En la forma let , la expression es un valor no monádico, mientras que el lado derecho de a <- es una expresión monádica. Por ejemplo, solo puede tener una operación de E / S (de tipo IO t ) en el segundo tipo de enlace. En detalle, las dos formas se pueden traducir aproximadamente como (donde ==> muestra la traducción):

do {let x = expression; rest} ==> let x = expression in do {rest}

y

do {x <- operation; rest} ==> operation >>= (/ x -> do {rest})


En un enlace let , la expresión puede tener cualquier tipo, y todo lo que está haciendo es darle un nombre (o un patrón que coincida con su estructura interna).

En la versión <- , la expresión debe tener el tipo ma , donde m es la mónada en que se encuentra el bloque do . Así que en la mónada IO , por ejemplo, los enlaces de esta forma deben tener algún valor del tipo IO a en la mano derecha lado. La parte a (dentro del valor monádico) es lo que está vinculado al patrón en el lado izquierdo. Esto le permite extraer los "contenidos" de la mónada dentro del alcance limitado del bloque do .

La notación de do es, como puede haber leído, solo azúcar sintáctica sobre los operadores de unión monádica ( >>= y >> ). x <- expression de azúcares a expression >>= /x -> y expression (por sí misma, sin los <- ) azúcares de expression >> . Esto solo proporciona una sintaxis más conveniente para definir largas cadenas de cómputos monádicos, que de otra manera tienden a construir una masa bastante impresionante de lambdas anidadas.

Deje let fijaciones no se deshidraten, en realidad. La única diferencia entre let un bloque do y let fuera de un bloque do es que la versión do no requiere la palabra clave in para seguirlo; los nombres que une están implícitamente en el alcance del resto del bloque do .


Haskell reconcilia la programación imperativa de efecto colateral con la programación funcional pura al representar acciones imperativas con tipos de forma IO a : el tipo de acción imperativa que produce un resultado de tipo a .

Una de las consecuencias de esto es que vincular una variable al valor de una expresión y vincularla al resultado de ejecutar una acción son dos cosas diferentes:

x <- action -- execute action and bind x to the result; may cause effect let x = expression -- bind x to the value of the expression; no side effects

Por getLine :: IO String tanto, getLine :: IO String es una acción, lo que significa que debe usarse de esta manera:

do line <- getLine -- side effect: read from stdin -- ...do stuff with line

Mientras que line1 ++ line2 :: String es una expresión pura, y debe usarse con let :

do line1 <- getLine -- executes an action line2 <- getLine -- executes an action let joined = line1 ++ line2 -- pure calculation; no action is executed return joined


La instrucción <- extraerá el valor de una mónada, y la declaración let no lo hará.

import Data.Typeable readInt :: String -> IO Int readInt s = do putStrLn $ "Enter value for " ++ s ++ ": " readLn main = do x <- readInt "x" let y = readInt "y" putStrLn $ "x :: " ++ show (typeOf x) putStrLn $ "y :: " ++ show (typeOf y)

Cuando se ejecuta, el programa le pedirá el valor de x, porque la acción monádica readInt "x" se ejecuta mediante la instrucción <- . No pedirá el valor de y, porque se readInt "y" pero no se ejecuta la acción monádica resultante.

Enter value for x: 123 x :: Int y :: IO Int

Desde x :: Int , puedes hacer cosas normales con él.

putStrLn $ "x = " ++ show x putStrLn $ "x * 2 = " ++ show (x * 2)

Desde y :: IO Int , no puedes pretender que es un Int normal.

putStrLn $ "y = " ++ show y -- ERROR putStrLn $ "y * 2 = " ++ show (y * 2) -- ERROR


let simplemente asignar un nombre a, o un patrón coincide en valores arbitrarios.

Para <- , primero alejémonos de la (no realmente) misteriosa mónada IO , pero consideremos las mónadas que tienen la noción de un "contenedor", como una lista o Maybe . Entonces <- no hace más que "desempaquetar" los elementos de ese contenedor. La operación opuesta de "devolverlo" es el return . Considere este código:

add m1 m2 = do v1 <- m1 v2 <- m2 return (v1 + v2)

"Desempaqueta" los elementos de dos contenedores, suma los valores y los envuelve de nuevo en la misma mónada. Trabaja con listas, tomando todas las combinaciones posibles de elementos:

main = print $ add [1, 2, 3] [40, 50] --[41,51,42,52,43,53]

De hecho, en el caso de listas, también podría escribir add m1 m2 = [v1 + v2 | v1 <- m1, v2 <- m2] add m1 m2 = [v1 + v2 | v1 <- m1, v2 <- m2] . Pero nuestra versión también funciona con Maybe s:

main = print $ add (Just 3) (Just 12) --Just 15 main = print $ add (Just 3) Nothing --Nothing

Ahora IO no es tan diferente en absoluto. Es un contenedor para un solo valor, pero es un valor impuro "peligroso" como un virus, que no debemos tocar directamente. El do Block está aquí en nuestra contención de vidrio, y los <- son los "guantes" incorporados para manipular las cosas que están dentro. Con la return , entregamos el contenedor completo e intacto (y no solo el contenido peligroso) cuando estamos listos. Por cierto, la función de add funciona con valores de IO (que obtuvimos de un archivo o la línea de comandos o un generador aleatorio ...) también.