13

下面的代码看起来很清楚:

do 
  x <- Just 3
  y <- Just "!"
  Just (show x ++ y)

这里xisNumyis的类型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)看起来很神秘......

有人对此有想法吗?

4

2 回答 2

16

好吧,你偶然发现了一种奇怪的单子。

有问题的单子是Monad ((->) r). 这是什么意思?嗯,它是表单函数的单子r -> *。即,采用相同类型输入的函数。

你问在这种情况下a和是什么类型。b好吧,它们都是Num a => a,但这并不能解释太多。

直观地说,我们可以这样理解 monad:monadic value 是一个函数,它接受一个 type 的值r作为输入。每当我们在 monad 中绑定时,我们都会获取该值并将其传递给绑定函数。

即,在我们的addStuff示例中,如果我们调用addStuff 5,则a绑定到(*2) 5(which is 10),并且b绑定到(+10) 5(which is 15)。

让我们从这个 monad 中看一个更简单的例子来尝试理解它是如何精确工作的:

mutate = do a <- (*2)
            return (a + 5)

如果我们将其脱糖为绑定,我们会得到:

mutate = (*2) >>= (\a -> return (a + 5))

现在,这并没有多大帮助,所以让我们为这个 monad使用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。

这是一个奇怪的单子。

于 2015-06-07T14:07:02.803 回答
9

给定addStuff

addStuff :: Int -> Int
addStuff = do
  a<-(*2)
  b<-(+10)
  return (a+b)

定义脱糖成

addStuff = 
    (* 2) >>= \a -> 
        (+ 10) >>= \b -> 
            return (a + b)

将鼠标悬停>>=fpcomplete 在线编辑器显示

:: 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 实例用于函数。确实,如果我们查看源代码,我们会看到

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)
于 2015-06-07T15:32:24.273 回答