Haskell函数组合报错咨询:为何f.g x无法正常运行?
为什么Haskell中
f.g x会报错而f(g x)正常? 先还原你的代码和遇到的报错:
f x = x + 3 g x = x * 3
报错信息:
<interactive>:17:1: error: • Non type-variable argument in the constraint: Num (a -> c) (Use FlexibleContexts to permit this) • When checking the inferred type it :: forall c a. (Num (a -> c), Num c) => a -> c
问题根源:运算符优先级和结合性
这个坑的核心是Haskell里函数组合运算符(.)的优先级比函数应用(直接写参数)低很多。
你大概率以为f.g x是先把f和g组合成一个新函数,再把x传给这个组合函数——也就是等价于(f . g) x,但实际上Haskell的语法规则里,函数应用的优先级是最高的,所以f.g x会被解析成:
f . (g x)
这就完全跑偏了:g x返回的是一个数值(比如g 2得到6),但函数组合(.)要求左右两边都必须是函数——它的类型是(b -> c) -> (a -> b) -> a -> c,左边需要接收一个b类型返回c的函数,右边需要接收一个a类型返回b的函数。现在右边是个数值,Haskell只能硬着头皮尝试把这个数值当成函数来处理,于是就出现了Num (a -> c)的错误——它试图让函数类型a -> c属于Num类型类,这显然不符合默认的类型规则。
而f(g x)是标准的函数应用写法,Haskell会先计算g x得到数值,再把这个数值传给f,完全匹配两个函数的类型,所以能正常运行。
解决办法
要让函数组合的写法正常工作,你有两个简单的选择:
加括号明确优先级:
把组合后的函数用括号括起来,确保先执行函数组合,再传递参数:(f . g) x用
$运算符降低优先级:$的优先级比(.)还低,它会让右边的表达式先计算完成,再交给左边的函数处理:f . g $ x
这两种写法都和f(g x)等价,也能正确利用函数组合的特性。
额外验证小技巧
你可以在GHCi里查看类型,就能更直观地理解差异:
- 输入
:t f . g,会得到Num a => a -> a,这是正确的组合函数类型; - 输入
:t f . (g 2),会得到Num (a -> c) => a -> c,这就是报错里的奇怪类型——因为g 2是数值,被错误地当成了函数参数传给(.)。
内容的提问来源于stack exchange,提问作者SAN
相关产品推荐
相关产品推荐

