Haskell函数定义语法

时间:2011-07-22 06:06:34

标签: functional-programming currying haskell

我正在通过以下方式进行列表连接(例如,使用GHC):

myConcat :: [[a]] -> [a]
myConcat xs = foldr (++) [] xs
myConcat    = foldr (++) []

有人可以向我解释一下上述定义为何以及如何运作,而且这个定义没有:

myConcat xs = foldr (++) []

有意不允许最后一行代码(由于构造可能会变得令人困惑,它没用,等等)或者它是更深层次的东西,可能与currying相关......

我希望我能对此有所了解,这真让我感到困惑:/

后期编辑:除了下面给出的解释之外,我已经找到了关于这个问题的一个很好的信息来源,来自Chap的"Partial function application and currying"部分。 4 {4}中的“功能编程”。这本书可以在线免费获得。

3 个答案:

答案 0 :(得分:7)

让我们回顾一下不同的版本:

myConcat xs = foldr (++) [] xs

这是通常的方式,提供由foldr消耗的参数。类型为[[a]] -> [a],因为我们在左侧有一个类型为[[a]]的参数,当输入到右侧时会产生[a]

myConcat = foldr (++) []

这里部分应用了foldr,所以我们返回一个函数,它可以带一个额外的参数,一个列表列表。所以我们从正确的方面回来的已经是我们需要的,它不是一种“语法上的混乱”,而是表达与第一个版本相同的另一种方式。类型再次为[[a]] -> [a]:我们左侧没有任何内容,但在右侧返回该签名的功能。

myConcat xs = foldr (++) []

这里foldr也被部分应用了,我们返回一个可以像以前一样接受参数的函数,但是我们的定义有一个额外的参数xs,它在右侧没有使用。编译器不“知道”我们想要应用于右侧的这个参数。类型为t -> [[a]] -> [a]。为什么呢?

假设你有一个方形函数:

sqr :: Int -> Int 
sqr x = x*x

您正在做的与提供一个未使用的附加参数基本相同:

sqr:: Int -> t -> Int 
sqr x y = x*x

该功能仍然“有效”,例如sqr 3 "bla"产生9,但类型签名关闭,未使用的参数是...... erm,未使用。未使用的参数没有固定类型,因为它实际上可以是“任何东西”,这无关紧要。因此它在签名中获得了类型变量(t)。

答案 1 :(得分:3)

好吧,我们来看看curried function foldr的类型签名:

>:t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b

因此foldr采用二元函数(即a->b->b),b值,a值列表,并返回b值。

让我们看一下foldr的{​​{3}}以获得更明确的定义:

  

foldr,应用于二元运算符,一个起始值(通常为   运算符的右侧标识)和列表,使用减少列表   二元运算符,从右到左:

现在,我们来看看myConcat xs = foldr (++) []

的类型签名
> :t myConcat
myConcat :: t -> [[a]] -> [a]
嗯......那不是我们想要的......

问题是您从未向foldr提供[a]类型的值。现在,myConcat需要一些任何类型的值来满足xs 类型为[a]的值才能完成foldr (++) [],例如:

> myConcat 2 [[1,2],[3,4]] 
[1,2,3,4]
> myConcat Nothing [[1,2],[3,4]] 
[1,2,3,4]

这很有效,但第一个论点只是浪费。

但是,如果我们将xs值传递给foldr (++) [],例如:

myConcat xs = foldr (++) [] xs

并检查其类型签名

> :t myConcat
myConcat :: [[a]] -> [a]
啊,好多了。现在myConcat使用xs来完成foldr函数。

此外,myConcat = foldr (++) []也有效,实际上是documentation的一个示例。如果我们检查foldr (++) []的类型签名,

> :t foldr (++) []
foldr (++) [] :: [[a]] -> [a]

由于我们已经通过point-free style programming提供了foldr前两个参数,因此我们得到一个返回的函数,它将获得[[a]]值并执行我们想要的操作!所以我们只是将它分配给一个名称,它就像上面的例子一样工作,但我们不需要显式传递参数!

> let myConcat = foldr (++) []
> :t myConcat
myConcat :: [[a]] -> [a]
> myConcat [[1,2],[3,4]]
[1,2,3,4]

答案 2 :(得分:2)

myConcat xs = foldr (++) []

的类型t -> [[a]] -> [a]与其他两个[[a]] -> [a]的类型不同。