2012-08-15 65 views
3

我刚起步的单子,我想不通为什么这两个表达式的计算结果是不同的:列表单子:>> =`和`return`行为之间的区别`

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") 
+3

如果你解释为什么你觉得他们应该是相同的您就有可能获得一些见解。 – Squidly 2012-08-15 15:36:43

回答

10

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

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

使用的[]如在单子的(>> =)表示,则推断该单子应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动作,在这种情况下(由于动作被返回)是一样的作为输入的结果。

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,找到列出单子实例,
  2. 第二更具体的类型表达

Here's单子实例:

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']) 

取决于这单子你”重新在。在列表单子,它只是变成了:

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

而在单子也许,它是:

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

这真的很有帮助;如果我可以标记第二个最喜欢的答案,我肯定会。 – planarian 2012-08-15 15:41:27

相关问题