2011-07-22 55 views
3

我做在以下几个方面列出了连接(例如,使用GHC):哈斯克尔函数定义语法

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

有人能向我解释,请为何以及如何对上述定义工作,这其中不:

myConcat xs = foldr (++) [] 

是最后一行代码故意不允许的(是有原因的,如结构可能会变得混乱,它是无用的,等等),或者是更深层次的东西,可能与讨好......

我希望我能提供一些线索这光线,这真的让我为难:/

以后编辑:除了下面给出的说明,我已经找到了一个很好的信息源上的事是从章一节"Partial function application and currying"。书籍"Real World Haskell"中的4“函数式编程”。这本书可以在网上免费下载。

回答

7

让我们回顾不同的版本:

myConcat xs = foldr (++) [] xs 

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

myConcat = foldr (++) [] 

这里foldr被部分应用,所以我们回到这可能需要一个额外的参数,列表列表的功能。所以我们从右边返回的东西已经是我们需要的东西了,它不是一个“语法suger”,而是表达与第一个版本相同的东西的另一种方式。该类型再次为[[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)。

+0

+1:一个非常容易理解的解释;你碰巧有一个Haskell相关的博客吗?我喜欢你的风格。 :-) –

+0

@Frerich Raabe:谢谢!事实上,我有一个博客,主要是关于Scala,不过现在又是Haskell,但不幸的是它是德文的:http://dgronau.wordpress。com/ – Landei

+0

非常感谢你,我现在明白了:)很好的解释 – Adi

2
myConcat xs = foldr (++) [] 

具有类型t -> [[a]] -> [a]这是不相同的其它两个[[a]] -> [a]类型。

3

好吧,让我们来看看为curried functionfoldr类型签名:

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

所以foldr取一个二元函数(即a->b->b),一个b值,a值的列表,并返回一个值为b

让我们也看看documentationfoldr以获得更明确的定义:

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 (++) []也有效,实际上是point-free style programming的一个例子。如果我们检查既然我们已经通过partial application提供foldr它的前两个参数的foldr (++) []类型签名,

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

,我们得到了一个功能回到那个意志需要[[a]]值,做我们想要的!所以我们只是将它分配给一个名称,它的工作方式就像上面的例子,但我们不需要明确地传递参数!

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

谢谢你的解释,我真的很感谢你的帮助:) – Adi