Haskell - 覆盖参数会导致意外行为

时间:2015-04-14 22:09:00

标签: haskell parameters

我写了一个简单的函数

someFunc list elem = do
    list <- elem:elem:elem:list
    return elem

现在,当我使用它时,我会得到像这样的输出

*Main> someFunc [] 'a'
"aaa"

尽管事实上,这个功能没有实际用途,为什么会发生?为什么编辑列表在elem中有效?以及如何为list分配新值以避免这种情况?

3 个答案:

答案 0 :(得分:4)

请注意,您的功能将被删除:

someFunc :: [b] -> b -> [b]
someFunc list elem = (elem:elem:elem:list) >>= \list -> return elem

现在请注意list中的\list -> return elem与传递给函数的输入list不同。

现在看看如何定义列表的Monad实例:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)  
    fail _ = []  

因此,您的代码最终会转换为此表单:

someFunc list elem = concat $ map (\list -> return elem) (elem:elem:elem:list)

现在您能理解为什么要获得该输出吗?

someFunc [] 'a'会像这样应用:

concat $ map (\list -> return 'a') ('a':'a':'a':[])
concat $ [['a'],['a'],['a']]
'a':'a':'a':[]
"aaa"

答案 1 :(得分:2)

您无法为list分配新值,list左侧的<-与{{1}不同,发生了什么?在list的右侧。如果您使用<-打开警告,则会看到

-Wall

您没有使用<interactive>:13:19: Warning: This binding for `elem' shadows the existing binding imported from `Prelude' (and originally defined in `GHC.List') <interactive>:14:7: Warning: This binding for `list' shadows the existing binding bound at <interactive>:13:14 <interactive>:14:7: Warning: Defined but not used: `list' 定义的名称list,只是定义它以便碰巧隐藏现有的绑定。

list <- ...返回someFunc [] 'a'的原因是由于列表monad的工作原理。这种表示法等同于

"aaa"

对于列表,someFunc list e = (e:e:e:list) >>= \l -> return e 基本上是>>=,所以你有

concatMap

所以用someFunc list e = concatMap (\l -> return e) (e:e:e:list) 代替[]list代替'a'我们得到

e

您的困惑可能来自someFunc [] 'a' = concatMap (\l -> return 'a') "aaa" = concat $ map (\l -> return 'a') "aaa" = concat [['a'], ['a'], ['a']] = ['a', 'a', 'a'] = "aaa" 的使用。在大多数语言中return是关键字,但在Haskell中它只是一个函数。它不会提前退出函数调用,它所做的就是在您所在的monad的上下文中包含一个值。对于列表return,这是整个定义。此外,在Haskell中,您无法重新分配值,但您可以使用新定义来隐藏它们。如果您始终使用return x = [x]-Wall进行编译,那么您将不会遇到此问题。

答案 2 :(得分:0)

您(显然是偶然)使用列表monad。在Python中,您的代码段将表示为:

for list in [elem, elem, elem] + list:
    yield elem

也就是说,您为列表中的每个项目创建一个包含一个elem的列表,另外还有三个。

以下是您的意图:

someFunc list elem = do
    list <- return (elem:elem:elem:list)
    return elem

这只会创建一个新的list变量遮蔽旧的变量,并完全忽略它以返回elem