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的类型是Numy的类型是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 -> * 形式的函数的单子。即采用相同类型输入的函数。

你问的是ab在这种情况下的类型。好吧,他们都是 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)