Haskell:在函数组合中使用map

时间:2012-05-06 13:39:38

标签: haskell syntax map function-composition

我对Haskell相对较新,如果我的问题听起来很愚蠢,那么道歉。我一直试图理解功能组合是如何工作的,我遇到了一个问题,我想知道有人可以帮助我。我在以下两个场景中使用函数组合中的map:

  • map (*2) . filter even [1,2,3,4]
  • map (*2) . zipWith max [1,2] [4,5]

尽管filter和zipWith函数都返回一个列表,但只有第一个组合有效,而第二个组合会抛出以下错误:

"Couldn't match expected type '[Int] -> [Int]' with actual type '[c0]'

任何建议都将不胜感激。

5 个答案:

答案 0 :(得分:17)

回想(.)

的类型
(.) :: (b -> c) -> (a -> b) -> a -> c

它有三个参数:两个函数和一个初始值,并返回两个函数的结果。

现在,函数对其参数的应用比(.)运算符更紧密。 所以你的表达:

map (*2) . filter even [1,2,3,4]

被解析为:

(.) (map (*2)) (filter even [1,2,3,4])

现在,第一个参数map (*2)没问题。它的类型为(b -> c),其中bcNum a => [a]。但是,第二个参数是一个列表:

Prelude> :t filter even [1,2,3,4]
filter even [1,2,3,4] :: Integral a => [a]

所以当[a]函数需要函数时,类型检查器会抱怨你传递(.)作为参数。

这就是我们所看到的:

Couldn't match expected type `a0 -> [b0]' with actual type `[a1]'
In the return type of a call of `filter'
In the second argument of `(.)', namely `filter even [1, 2, 3, 4]'
In the expression: map (* 2) . filter even [1, 2, 3, 4]

所以......括号!

使用$运算符添加括号:

map (*2) . filter even $ [1,2,3,4]

或使用显式的parens,删除两个函数的组合

map (*2) (filter even [1,2,3,4])

甚至:

(map (*2) . filter even) [1,2,3,4]

答案 1 :(得分:5)

zipWith max [1,2] [4,5]的结果是列表,而不是函数。 (。)运算符需要一个函数作为其右操作数。因此你的第二行错误。可能你想要的是

map (*2) (zipWith max [1,2] [4,5])

你的第一个例子不能在WinHugs上编译(拥抱模式);它有同样的错误。以下将起作用

(map (*2) . filter even) [1,2,3,4]

因为它组成了两个函数并将结果函数应用于参数。

答案 2 :(得分:5)

以下表格有效:

map (* 2) $ filter even [1, 2, 3, 4]
(map (* 2) . filter even) [1, 2, 3, 4]
map (* 2) $ zipWith max [1, 2] [4, 5]
(\xs -> map (* 2) . zipWith max xs) [1, 2] [4, 5]

但不是以下内容:

map (* 2) . filter even [1, 2, 3, 4]
map (* 2) . zipWith max [1, 2] [4, 5]
(map (* 2) . zipWith max) [1, 2] [4, 5]

为什么会这样?好吧,举个例子

map (* 2) . zipWith max [1, 2] [4, 5]

相同
(map (* 2)) . (((zipWith max) [1, 2]) [4, 5])

(map (* 2))的类型为[Int] -> [Int](假设默认为Int),(((zipWith max) [1, 2]) [4, 5])的类型为[Int](.)的类型为(b -> c) -> (a -> b) -> a -> c在这种非多态的情况下,或([Int] -> [Int]) -> ([Int] -> [Int]) -> [Int] -> [Int],所以这是错误的类型。另一方面,($)在此非多态情况下具有(a -> b) -> a -> b([Int] -> [Int]) -> [Int] -> [Int]类型,因此:

(map (* 2)) $ (((zipWith max) [1, 2]) [4, 5])

打字很好。

答案 3 :(得分:2)

由于(.)的优先级较低,Haskell解析

map (*2) . filter even [1,2,3,4]

as

map (*2) . (filter even [1,2,3,4])

即。用map (*2)(一个列表)的结果组合filter even [1,2,3,4](一个函数),这是没有意义的,并且是一个类型错误。

您可以使用@ Theodore的建议或使用($)

来解决此问题
map (*2) . filter even $ [1,2,3,4]

答案 4 :(得分:2)

如果您检查地图类型:(a -> b) -> [a] -> [b]

因此,它需要a的函数进入b然后是a的列表并返回b的列表。正确?

现在,您已经通过传递参数(*2)提供了a到b的函数。因此,部分应用的地图函数最终为:[Integer] -> [Integer]意味着您将收到一个整数列表并返回一个整数列表。

到目前为止,您可以撰写(。)具有相同签名的函数。如果你检查filter even的类型是什么,你会看到它是:[Integer] -> [Integer],这里是一个有效的合成候选者。

如果您检查类型:map (*2) . filter even它是[Integer] -> [Integer]

,那么此合成不会改变函数的最终签名。

map (*2) . zipWith max [1,2] [4,5]不会出现这种情况,因为zipWith maxmap (*2)所期望的签名不同。