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

pla*_*ian 3 monads haskell

我刚刚开始使用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")
Run Code Online (Sandbox Code Playgroud)

Phy*_*hyx 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)
Run Code Online (Sandbox Code Playgroud)

是相同的

concatMap (\n -> concatMap (\ch -> [(n, ch)]) ['a', 'b']) [1,2]
Run Code Online (Sandbox Code Playgroud)

返回 [(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])
Run Code Online (Sandbox Code Playgroud)

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

t也是默认的Integer,因此得到的表达式的类型是:: IO ([Integer], [Char]).

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


Mat*_*ick 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])
Run Code Online (Sandbox Code Playgroud)

因此,它们具有不同的价值.

也许你的单子的存在混淆里面的参数return.但是,看看它的类型:

ghci> :t return
return :: Monad m => a -> m a
Run Code Online (Sandbox Code Playgroud)

return 对它的参数一无所知 - 它只需要一个值,任何值,并将它放在一个默认的monadic上下文中.


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

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

这是 monad实例:

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

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

那么让我们扩展我们的表达:

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

并且return很容易摆脱:

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

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

return ([1,2],['a','b'])
Run Code Online (Sandbox Code Playgroud)

取决于你所在的monad.在monad列表中,它简单地变为:

[([1,2], ['a','b'])] :: [] ([Int], String)
Run Code Online (Sandbox Code Playgroud)

而在Maybe monad中,它是:

Just ([1,2], ['a', 'b']) :: Maybe ([Int], String)
Run Code Online (Sandbox Code Playgroud)