无法获得在Haskell中编译的无点表示法

时间:2014-09-03 15:07:43

标签: haskell pointfree function-composition

这是有效的

unique :: (a -> Bool) -> [a] -> Bool
unique p xs = 1 == length (filter p xs)

但现在我想要它的形式:

unique = (== 1) . length . filter

错误讯息:

Couldn't match expected type `[a] -> Bool' with actual type `Bool'
Expected type: b0 -> [a] -> Bool
  Actual type: b0 -> Bool
In the first argument of `(.)', namely `(== 1)'
In the expression: (== 1) . length . filter

为什么这不起作用?

1 个答案:

答案 0 :(得分:6)

这是因为filter是一个双参数函数。你可以使用方便的操作员来解决这个问题

(.:) = (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.) . (.)

-- Important to make it the same precedence as (.)
infixr 9 .:

unique = ((== 1) . length) .: filter

如果你看一下GHCi中(length .)的类型,你就会得到

(length .) :: (a -> [b]) -> a -> Int

这意味着它需要一个返回列表的单参数函数。如果我们查看filter的类型:

filter :: (a -> Bool) -> [a] -> [a]

可以将其重写为“单个参数”为

filter :: (a -> Bool) -> ([a] -> [a])

这显然不符合a -> [b]!特别是,编译器无法弄清楚如何使([a] -> [a])[b]相同,因为一个是列表上的函数,另一个只是一个列表。所以这是类型错误的来源。


有趣的是,.:运算符可以推广到编写仿函数:

(.:) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
(.:) = fmap fmap fmap
-- Since the first `fmap` is for the (->) r functor, you can also write this
-- as (.:) = fmap `fmap` fmap === fmap . fmap

这有什么好处?假设你有一个Maybe [[Int]],如果它存在,你想要Just中每个子列表的总和:

> let myData = Just [[3, 2, 1], [4], [5, 6]]
> sum .: myData
Just [6, 4, 11]
> length .: myData
Just [3, 1, 2]
> sort .: myData
Just [[1,2,3],[4],[5,6]]

或者如果你有一个[Maybe Int],你想增加每一个:

> let myData = [Just 1, Nothing, Just 3]
> (+1) .: myData
[Just 2,Nothing,Just 4]

可能性一直在继续。基本上,它允许您在两个嵌套仿函数中映射函数,并且这种结构经常出现。如果您在Maybe内列出了一个列表,或列表中有元组,或者IO返回一个字符串,或类似的内容,那么您会遇到可以使用{{{ 1}}。