Haskell中如何推断"(not .) . any"的类型?
all/none/any实现的两个疑问 我正在通过Codewars学习Haskell,对应的题目要求实现all、none、any三个函数,参考实现代码如下:
module Codewars.Kata.AllNoneAny where import Prelude hiding (all, any) all, none, any :: (a -> Bool) -> [a] -> Bool all = (and .) . map none = (not .) . any any = (or .) . map
疑问1
由于(.)是infixr 9优先级的右结合运算符,我认为应先组合(. any),再组合(not .),这个理解是否正确?
疑问2
如何将类型为(a -> Bool) -> a -> Bool的(not .)与类型为(([a] -> Bool) -> c) -> (a -> Bool) -> c的(. any)组合成类型为(a -> Bool) -> [a] -> Bool的(not .) . any?
相关类型定义:
not :: Bool -> Bool (.) :: (b -> c) -> (a -> b) -> (a -> c) any :: (a -> Bool) -> [a] -> Bool (. any) :: (([a] -> Bool) -> c) -> (a -> Bool) -> c (not .) :: (a -> Bool) -> a -> Bool (not .) . any :: (a -> Bool) -> [a] -> Bool
解答
针对疑问1:理解错误,正确结合顺序是(not .)与any直接组合
(.)是右结合运算符,但这里的表达式结构是( (not .) ) . any——(not .)是一个完整的函数(可以理解为“接收一个返回Bool的函数,返回它的取反版本”),any是另一个独立函数。右结合的规则是针对连续的.链式组合(比如f . g . h会解析为f . (g . h)),但这里不存在嵌套组合(. any)的情况,你之前的拆分是错误的,不是not和(. any)组合,而是(not .)和any直接组合。
针对疑问2:通过函数组合的类型匹配规则推导
首先纠正一个小偏差:(not .)的实际类型应该是(x -> Bool) -> (x -> Bool),而非你标注的(a -> Bool) -> a -> Bool。(not .)等价于\f x -> not (f x),即接收一个返回Bool的函数f,返回一个新函数——先执行f再对结果取反。
现在推导(not .) . any的类型:
any的类型是(a -> Bool) -> ([a] -> Bool):它接收一个谓词p :: a -> Bool,返回一个检查列表的函数check :: [a] -> Bool。(not .)的类型是(x -> Bool) -> (x -> Bool):它接收一个返回Bool的函数(比如上面的check),返回其取反版本\xs -> not (check xs)。- 函数组合
(.)的核心是匹配左函数的输入类型与右函数的输出类型:any的输出[a] -> Bool正好是(not .)的输入类型,因此组合后的函数逻辑是:- 先接收
p :: a -> Bool,传给any得到check :: [a] -> Bool - 再把
check传给(not .),得到\xs -> not (check xs) - 整体类型就是
(a -> Bool) -> ([a] -> Bool),也就是(a -> Bool) -> [a] -> Bool,完全符合none的类型要求。
- 先接收
本质上(not .) . any等价于\p xs -> not (any p xs),和none的语义完全一致。
内容的提问来源于stack exchange,提问作者zichao liu

