Haskell 中 do-notation 中的变量类型是什么?
What is the type of the variable in do-notation here in Haskell?
下面的代码看起来很清楚:
do
x <- Just 3
y <- Just "!"
Just (show x ++ y)
这里x
的类型是Num
,y
的类型是String
。 (这里的<-
用于从Monad中取出实际值)
但是,这段代码对我来说不是很清楚:
import Control.Monad.Instances
addStuff :: Int -> Int
addStuff = do
a <- (* 2)
b <- (+ 10)
return (a + b)
这里a
的类型和b
的类型是什么?看起来他们的行为像 Num
,但是 a <- (* 2)
和 b <- (+ 10)
在这里看起来很神秘...
有人对此有想法吗?
嗯,你偶然发现了一种奇怪的 monad。
有问题的 monad 是 Monad ((->) r)
。这是什么意思?好吧,它是 r -> *
形式的函数的单子。即采用相同类型输入的函数。
你问的是a
和b
在这种情况下的类型。好吧,他们都是 Num a => a
,但这并不能真正解释太多。
直观上,我们可以这样理解monad:monadic value是一个以r
类型的值作为输入的函数。每当我们在 monad 中绑定时,我们都会获取该值并将其传递给绑定函数。
即,在我们的 addStuff
示例中,如果我们调用 addStuff 5
,则 a
绑定到 (*2) 5
(即 10
),并且b
绑定到 (+10) 5
(即 15
)。
让我们从这个 monad 中看一个更简单的示例,以尝试理解它的精确工作原理:
mutate = do a <- (*2)
return (a + 5)
如果我们将其脱糖为绑定,我们得到:
mutate = (*2) >>= (\a -> return (a + 5))
现在,这没什么帮助,所以让我们为这个 monad 使用 the definition of bind:
mutate = \ r -> (\a -> return (a + 5)) ((*2) r) r
这减少到
mutate = \ r -> return ((r*2) + 5) r
我们用return
定义为const
,可以化简为
mutate = \ r -> (r*2) + 5
这是一个函数,将一个数乘以 2,然后加 5。
那是一个奇怪的单子。
鉴于addStuff
addStuff :: Int -> Int
addStuff = do
a<-(*2)
b<-(+10)
return (a+b)
定义脱糖成
addStuff =
(* 2) >>= \a ->
(+ 10) >>= \b ->
return (a + b)
将鼠标悬停在 fpcomplete online editor 中的 >>=
上显示
:: 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)
这让我们相信我们使用 Monad 实例作为函数。事实上,如果我们查看 source code,我们会看到
instance Monad ((->) r) where
return = const
f >>= k = \ r -> k (f r) r
使用这个新获得的信息,我们可以自己评估 addStuff
函数。
给定初始表达式
(* 2) >>= ( \a -> (+10) >>= \b -> return (a + b) )
我们用>>=
的定义代入,给我们(下面{}
、[]
、()
只是说明()
的不同深度)
\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {(* 2) r1} r1
简化最外层 lambda 中的倒数第二项
\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {r1 * 2} r1
将{r1 * 2}
应用到{\a -> ...}
\r1 -> {(+10) >>= \b -> return ((r1 * 2) + b)} r1
用它的定义再次替换剩余的 >>=
\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [(+10) r2] r2} r1
简化内部 lambda 中的倒数第二项
\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [r2 + 10] r2} r1
将[r2 + 10]
应用到{\b -> ...}
\r1 -> {\r2 -> [return (r1 * 2 + (r2 + 10))] r2} r1
将r1
应用到{\r2 -> ...}
\r1 -> {return (r1 * 2 + r1 + 10) r1}
用定义
替换return
\r1 -> {const (r1 * 2 + r1 + 10) r1}
评价const x _ = x
\r1 -> {r1 * 2 + r1 + 10}
美化
\x -> 3 * x + 10
终于得到了
addStuff :: Int -> Int
addStuff = (+ 10) . (* 3)
下面的代码看起来很清楚:
do
x <- Just 3
y <- Just "!"
Just (show x ++ y)
这里x
的类型是Num
,y
的类型是String
。 (这里的<-
用于从Monad中取出实际值)
但是,这段代码对我来说不是很清楚:
import Control.Monad.Instances
addStuff :: Int -> Int
addStuff = do
a <- (* 2)
b <- (+ 10)
return (a + b)
这里a
的类型和b
的类型是什么?看起来他们的行为像 Num
,但是 a <- (* 2)
和 b <- (+ 10)
在这里看起来很神秘...
有人对此有想法吗?
嗯,你偶然发现了一种奇怪的 monad。
有问题的 monad 是 Monad ((->) r)
。这是什么意思?好吧,它是 r -> *
形式的函数的单子。即采用相同类型输入的函数。
你问的是a
和b
在这种情况下的类型。好吧,他们都是 Num a => a
,但这并不能真正解释太多。
直观上,我们可以这样理解monad:monadic value是一个以r
类型的值作为输入的函数。每当我们在 monad 中绑定时,我们都会获取该值并将其传递给绑定函数。
即,在我们的 addStuff
示例中,如果我们调用 addStuff 5
,则 a
绑定到 (*2) 5
(即 10
),并且b
绑定到 (+10) 5
(即 15
)。
让我们从这个 monad 中看一个更简单的示例,以尝试理解它的精确工作原理:
mutate = do a <- (*2)
return (a + 5)
如果我们将其脱糖为绑定,我们得到:
mutate = (*2) >>= (\a -> return (a + 5))
现在,这没什么帮助,所以让我们为这个 monad 使用 the definition of bind:
mutate = \ r -> (\a -> return (a + 5)) ((*2) r) r
这减少到
mutate = \ r -> return ((r*2) + 5) r
我们用return
定义为const
,可以化简为
mutate = \ r -> (r*2) + 5
这是一个函数,将一个数乘以 2,然后加 5。
那是一个奇怪的单子。
鉴于addStuff
addStuff :: Int -> Int
addStuff = do
a<-(*2)
b<-(+10)
return (a+b)
定义脱糖成
addStuff =
(* 2) >>= \a ->
(+ 10) >>= \b ->
return (a + b)
将鼠标悬停在 fpcomplete online editor 中的 >>=
上显示
:: 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)
这让我们相信我们使用 Monad 实例作为函数。事实上,如果我们查看 source code,我们会看到
instance Monad ((->) r) where
return = const
f >>= k = \ r -> k (f r) r
使用这个新获得的信息,我们可以自己评估 addStuff
函数。
给定初始表达式
(* 2) >>= ( \a -> (+10) >>= \b -> return (a + b) )
我们用>>=
的定义代入,给我们(下面{}
、[]
、()
只是说明()
的不同深度)
\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {(* 2) r1} r1
简化最外层 lambda 中的倒数第二项
\r1 -> {\a -> (+10) >>= \b -> return (a + b)} {r1 * 2} r1
将{r1 * 2}
应用到{\a -> ...}
\r1 -> {(+10) >>= \b -> return ((r1 * 2) + b)} r1
用它的定义再次替换剩余的 >>=
\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [(+10) r2] r2} r1
简化内部 lambda 中的倒数第二项
\r1 -> {\r2 -> [\b -> return (r1 * 2 + b)] [r2 + 10] r2} r1
将[r2 + 10]
应用到{\b -> ...}
\r1 -> {\r2 -> [return (r1 * 2 + (r2 + 10))] r2} r1
将r1
应用到{\r2 -> ...}
\r1 -> {return (r1 * 2 + r1 + 10) r1}
用定义
替换return
\r1 -> {const (r1 * 2 + r1 + 10) r1}
评价const x _ = x
\r1 -> {r1 * 2 + r1 + 10}
美化
\x -> 3 * x + 10
终于得到了
addStuff :: Int -> Int
addStuff = (+ 10) . (* 3)