what - monad haskell
¿Cuál es el tipo de la variable en do-notation aquí en Haskell? (2)
Los códigos a continuación parecen bastante claros:
do
x <- Just 3
y <- Just "!"
Just (show x ++ y)
Aquí el tipo de x
es Num
e y
es String
. ( <-
aquí se usa para sacar valor real de la Mónada)
Sin embargo, este fragmento no parece tan claro para mí:
import Control.Monad.Instances
addStuff :: Int -> Int
addStuff = do
a <- (* 2)
b <- (+ 10)
return (a + b)
¿Cuál es el tipo de a
y tipo de b
aquí? Parece que actúan como un Num
, pero a <- (* 2)
b <- (+ 10)
parece críptico aquí ...
¿Alguien tiene ideas sobre esto?
Bueno, te has tropezado con una especie de mónada extraña.
La mónada en cuestión es la Monad ((->) r)
. Ahora, ¿qué significa eso? Bueno, es la mónada de funciones de la forma r -> *
. Es decir, de funciones que toman el mismo tipo de entrada.
Usted preguntó qué tipo de a
y b
son en este caso. Bueno, ambos son Num a => a
, pero eso realmente no explica mucho.
Intuitivamente, podemos entender la mónada de esta manera: Un valor monádico es una función que toma un valor de tipo r
como entrada. Siempre que nos atemos a la mónada, tomamos ese valor y lo pasamos a la función ligada.
Es decir, en nuestro ejemplo addStuff
, si llamamos a addStuff 5
, entonces a
está obligado a (*2) 5
(que es 10
), y b
está vinculado a (+10) 5
(que es 15
).
Veamos un ejemplo más simple de esta mónada para tratar de entender cómo funciona exactamente:
mutate = do a <- (*2)
return (a + 5)
Si deshacemos esto para un compromiso, obtenemos:
mutate = (*2) >>= (/a -> return (a + 5))
Ahora, esto no ayuda mucho, así que usemos la definición de bind para esta mónada:
mutate = / r -> (/a -> return (a + 5)) ((*2) r) r
Esto se reduce a
mutate = / r -> return ((r*2) + 5) r
Lo cual usando la definición que return
es const
, se puede reducir a
mutate = / r -> (r*2) + 5
Que es una función, que multiplica un número por 2, y luego agrega 5.
Esa es una mónada extraña.
Dado addStuff
addStuff :: Int -> Int
addStuff = do
a<-(*2)
b<-(+10)
return (a+b)
la definición desciende en
addStuff =
(* 2) >>= /a ->
(+ 10) >>= /b ->
return (a + b)
Pasar el cursor sobre el >>=
en el editor en línea fpcomplete muestra
:: Monad m => forall a b.
(m a ) -> (a -> m b ) -> (m b )
:: (Int -> a ) -> (a -> Int -> b ) -> (Int -> b )
:: (Int -> Int) -> (Int -> Int -> Int) -> (Int -> Int)
Eso nos lleva a creer que usamos una instancia de Monad para funciones. De hecho, si miramos el código fuente , vemos
instance Monad ((->) r) where
return = const
f >>= k = / r -> k (f r) r
Usando esta información recién obtenida podemos evaluar la función addStuff
nosotros mismos.
Dada la expresión inicial
(* 2) >>= ( /a -> (+10) >>= /b -> return (a + b) )
lo sustituimos usando la definición >>=
, dándonos (en el siguiente {}
, []
, ()
solo ilustra diferente profundidad de ()
)
/r1 -> {/a -> (+10) >>= /b -> return (a + b)} {(* 2) r1} r1
simplificar el penúltimo término dentro de la lambda más externa
/r1 -> {/a -> (+10) >>= /b -> return (a + b)} {r1 * 2} r1
aplicar {r1 * 2}
a {/a -> ...}
/r1 -> {(+10) >>= /b -> return ((r1 * 2) + b)} r1
sustituir el resto >>=
con su definición nuevamente
/r1 -> {/r2 -> [/b -> return (r1 * 2 + b)] [(+10) r2] r2} r1
simplifica el penúltimo término dentro de la lambda interior
/r1 -> {/r2 -> [/b -> return (r1 * 2 + b)] [r2 + 10] r2} r1
aplicar [r2 + 10]
a {/b -> ...}
/r1 -> {/r2 -> [return (r1 * 2 + (r2 + 10))] r2} r1
aplicar r1
a {/r2 -> ...}
/r1 -> {return (r1 * 2 + r1 + 10) r1}
return
sustitutivo con su definición
/r1 -> {const (r1 * 2 + r1 + 10) r1}
evaluar const x _ = x
/r1 -> {r1 * 2 + r1 + 10}
embellecer
/x -> 3 * x + 10
finalmente obtenemos
addStuff :: Int -> Int
addStuff = (+ 10) . (* 3)