Haskell中的$是什么,如何对函数应用函数

What is $ in Haskell, and how to apply functions to functions

我正在用 Haskell 做 Project Euler,在完成 the very first problem 时发现了一些可以学习的东西。这是我的代码:

isValid x = (mod x 3 == 0) || (mod x 5 == 0)
listValid :: Integer -> [Integer]
listValid n = [x | x <- [1..n-1], isValid x]

函数 listValid 将得到所有小于 n 且能被 3 或 5 整除的正整数。很简单。

*Main> listValid 10
[3,5,6,9]

现在我需要对它们求和。我认为 sum 函数是执行此操作的正确方法。我不明白的是为什么前两个版本有效,而第三个版本无效。

*Main> sum (listValid 10)
23
*Main> sum $ listValid 10
23
*Main> sum listValid 10

<interactive>:4:5:
    Couldn't match type ‘[Integer]’ with ‘a0 -> t’
    Expected type: Integer -> a0 -> t
      Actual type: Integer -> [Integer]
    Relevant bindings include it :: t (bound at <interactive>:4:1)
    In the first argument of ‘sum’, namely ‘listValid’
    In the expression: sum listValid 10

这是操作顺序问题吗,我需要用括号括起来以断言应该首先应用哪个函数?如果是这样,$ 在第二个版本中做什么?

函数应用f x是最高优先级的操作(和左关联),因此

sum listValid 10

等同于(sum listValid) 10.

另一方面,$ 运算符具有最低的优先级(并且是右结合的,尽管此处不相关),因此

sum $ listValid 10

隐含地与 sum $ (listValid 10) 相同,而不是 (sum $ listValid) 10。因此,它通常用于消除括号。

这是关于关联性的。函数应用是左结合的,所以 sum listValid 10 等价于 (sum listValid) 10,而不是 sum (listValid 10)。如果你考虑一下,它必须是这样的:如果你定义 add x y = x+y,你不会希望 add 1 2 等同于 add (1 2).

所以这里的问题是在 sum listValid 10 中,它没有将 listValid 10 视为 sum 的参数;它将 listValid 视为 sum 的参数,然后 10 作为 sum listValid.

的参数

$ 解决了这个问题,因为它是一个中缀运算符,而且很明显 sum 是它的左操作数,而 listValid 10 是它的右操作数(记住函数应用程序有优先级高于任何中缀运算符,因此它不能被视为 (sum $ listValid) 10).

当你写f $ x时,你实际上写了($) f x($) :: (a -> b) -> a -> b a function. This function is defined as:

($) :: forall r a (b :: TYPE r). (a -> b) -> a -> b
f $ x = f x

以上看起来不是很令人印象深刻。这样写f $ x就相当于f x,那为什么还要写$呢?因为这个运算符有precedence 0。这意味着如果你写:

f $ x+2

解释为:

($) f (x+2)

因此:

f (x+2)

不需要写括号。

回到你的问题,如果你写:

sum $ listValid 10

这被解析为:

($) (sum) (listValid 10)

因此在功能上等同于:

sum (listValid 10)

如果你写:

sum listValid 10

Haskell 解释为:

(sum listValid) 10

现在 Integer -> [Integer] 类型的函数的 sum 没有意义,sum :: Num a => [a] -> a 应该采用 Numerical 值的列表,因此出现错误。

函数应用是左结合的,所以

f x y

解析为:

(f x) y

但是,函数应用程序的优先级高于任何中缀运算符,因此

f x $ g y

解析为:

(f x) $ (g y)

特别是,您有:

sum listValid 10 = (sum listValid) 10
sum $ listValid 10 = sum $ (listValid 10) = sum (listValid 10)