列出monad:`>> =`和`return`行为之间的区别

时间:2012-08-15 13:28:08

标签: haskell monads

我刚刚开始使用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")

2 个答案:

答案 0 :(得分: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尝试打印结果时,在这种情况下m Monad将为IO

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

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

答案 1 :(得分: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对其参数一无所知 - 只需要一个值,任何值,并将其置于默认的monadic上下文中。


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

  1. Hoogle,找到列表的monad实例,
  2. 第二个表达式的更具体类型
  3. Here's monad实例:

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

    (我们可以忽略>>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'])
    

    取决于你在哪个monad 。在monad列表中,它只是变成:

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

    而在Maybe monad中,它是:

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