3

我刚刚开始使用 monad,我无法弄清楚为什么这两个表达式的计算方式不同:

ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]


ghci> return ([1,2],['a','b'])
([1,2],"ab")
4

2 回答 2

10

类型不同,所以行为不同是合理的

第一个表达式将类型检查为Num t => [(t, Char)]

在 (>>=) 中使用 [] 作为 monad 意味着它推断出 monad 应该是 List monad,并且在List Monad http://en.wikibooks.org/wiki/Haskell/Understanding_monads/List的上下文中(>>=) 是 concatMap 并且返回是 (:[])。

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)

是相同的

concatMap (\n -> concatMap (\ch -> [(n, ch)]) ['a', 'b']) [1,2]

返回[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

在你的第二个例子中,真正发生的是

表达式的类型更一般:

Prelude> :t return ([1,2],['a','b'])
return ([1,2],['a','b']) :: (Monad m, Num t) => m ([t], [Char])

因为你在 GHCi 中运行它,所以会发生一些事情。GHCi 可以认为是一个非常大的特殊 IO Monad。因此,由于没有指定 monad,所以当 GHC 尝试打印结果时,在这种情况下将采用mMonad 。IO

t也默认为 ,因此Integer结果表达式的类型为:: IO ([Integer], [Char])

碰巧,所有使用的类型都有一个Show实例,因此 GHC 可以打印执行IO动作的结果,在这种情况下(由于动作被返回)与输入相同。

于 2012-08-15T13:52:52.987 回答
6

在 GHCi 中,您可以使用:t. 这样做表明您的表达式具有不同的类型:

ghci> :t [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
  :: (Num t) => [(t, Char)]

ghci> :t return ([1,2],['a','b'])
return ([1,2],['a','b']) :: (Num t, Monad m) => m ([t], [Char])

因此,它们具有不同的值。

也许return. 但是,看看它的类型:

ghci> :t return
return :: Monad m => a -> m a

return对它的论点一无所知——它只接受一个值,任何值,并将其置于默认的单子上下文中。


要准确了解评估这些表达式时会发生什么,您需要:

  1. Hoogle,查找列表的 monad 实例,以及
  2. 第二个表达式的更具体的类型

这是monad 实例:

instance  Monad []  where
    m >>= k             = foldr ((++) . k) [] m
    m >> k              = foldr ((++) . (\ _ -> k)) [] m
    return x            = [x]
    fail _              = []

(我们可以忽略>>and fail,因为我们没有使用它们。)

所以让我们扩展我们的表达式:

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)

这样设置m = [1, 2]k = \n -> ['a','b'] >>= \ch -> return (n,ch)我们得到:

foldr ((++) . (\n -> ['a','b'] >>= \ch -> return (n,ch))) [] [1,2]

现在摆脱第二个>>=m = ['a', 'b']并且k = \ch -> return (n, ch)

foldr ((++) . (\n -> rest)) [] [1,2]
  where
    rest = foldr ((++) . (\ch -> return (n,ch))) [] ['a', 'b']

并且return很容易摆脱:

foldr ((++) . (\n -> rest)) [] [1,2]
  where
    rest = foldr ((++) . (\ch -> [(n,ch)]) [] ['a', 'b']

另一方面,第二个表达式的值:

return ([1,2],['a','b'])

取决于你在哪个单子。在 list monad 中,它简单地变成:

[([1,2], ['a','b'])] :: [] ([Int], String)

而在 Maybe monad 中,它是:

Just ([1,2], ['a', 'b']) :: Maybe ([Int], String)
于 2012-08-15T13:57:19.160 回答