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

kai*_*ker 2 haskell pointfree function-composition

这很有效

unique :: (a -> Bool) -> [a] -> Bool
unique p xs = 1 == length (filter p xs)
Run Code Online (Sandbox Code Playgroud)

但现在我想要它的形式:

unique = (== 1) . length . filter
Run Code Online (Sandbox Code Playgroud)

错误信息:

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
Run Code Online (Sandbox Code Playgroud)

为什么这不起作用?

bhe*_*ilr 6

这是因为filter是两个参数函数.您可以使用方便的操作员来解决这个问题

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

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

unique = ((== 1) . length) .: filter
Run Code Online (Sandbox Code Playgroud)

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

(length .) :: (a -> [b]) -> a -> Int
Run Code Online (Sandbox Code Playgroud)

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

filter :: (a -> Bool) -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

这可以被重写,使其成为"单一参数"

filter :: (a -> Bool) -> ([a] -> [a])
Run Code Online (Sandbox Code Playgroud)

这显然不符合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
Run Code Online (Sandbox Code Playgroud)

这有什么用?假设你有一个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]]
Run Code Online (Sandbox Code Playgroud)

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

> let myData = [Just 1, Nothing, Just 3]
> (+1) .: myData
[Just 2,Nothing,Just 4]
Run Code Online (Sandbox Code Playgroud)

可能性一直在继续.基本上,它允许您在两个嵌套仿函数中映射函数,并且这种结构经常出现.如果你在列表中有一个列表Maybe,或列表中的元组,或者IO返回一个字符串,或类似的东西,你会遇到一个你可以使用的情况(.:) = fmap fmap fmap.

  • `unique =(== 1).:length.:filter`也是可能的.另一方面`(== 1).length.:filter ::(Num([a] - > Int),Eq([a] - > Int))=>(a - > Bool) - > Bool`可能是typecheck但没用. (2认同)