函数应用在Haskell和()之间的区别。

Function application in Haskell and the difference between (), . and $

本问题已经有最佳答案,请猛点这里访问。

there are different function of Application方式在Haskell,but that each has its specificities似乎。我想了解$between the,and,.():P></

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Prelude> 100 / fromIntegral( length [1..10] )
10.0
Prelude> 100 / fromIntegral . length  [1..10]

<interactive>:193:22: error:
    * Couldn't match expected type `a -> Integer'
                  with actual type `Int'
    * Possible cause: `length' is applied to too many arguments
      In the second argument of `(.)', namely `length [1 .. 10]'
      In the second argument of `(/)', namely
        `fromIntegral . length [1 .. 10]'
      In the expression: 100 / fromIntegral . length [1 .. 10]
    * Relevant bindings include
        it :: a -> c (bound at <interactive>:193:1)
Prelude> 100 / fromIntegral $ length [1..10]

<interactive>:194:1: error:
    * Non type-variable argument
        in the constraint: Fractional (Int -> b)
      (Use FlexibleContexts to permit this)
    * When checking the inferred type
        it :: forall b. (Num b, Fractional (Int -> b)) => b
Prelude>

为什么不同的行为在this is the only parentheses和房屋,()工作?P></


其中只有一种实际上是内置的函数应用程序语法:您称之为()。(注意,它实际上不需要任何括号,只是并列。)所有haskell表达式最终都归结为这种语法;中缀运算符样式只是它的语法糖。您尝试过的示例如下:

1
2
3
4
5
6
??: 100 / fromIntegral( length [1..10] )
      ≡ (/) 100 (fromIntegral (length [1..10]))
??: 100 / fromIntegral . length [1..10]
      ≡ (/) 100 ((.) fromIntegral (length [1..10]))
??: 100 / fromIntegral $ length [1..10]
      ≡ ($) ((/) 100 fromIntegral) (length [1..10])

它们之所以如此不同,是因为中缀优先规则:每个中缀运算符都有一个固定性声明。与此相关的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Prelude> :i .
(.) :: (b -> c) -> (a -> b) -> a -> c   -- Defined in ‘GHC.Base’
infixr 9 .
Prelude> :i $
($) ::
  forall (r :: GHC.Types.RuntimeRep) a (b :: TYPE r).
  (a -> b) -> a -> b
    -- Defined in ‘GHC.Base’
infixr 0 $
Prelude> :i /
class Num a => Fractional a where
  (/) :: a -> a -> a
  ...
    -- Defined in ‘GHC.Real’
infixl 7 /

这告诉我们:./结合得更紧密,后者比$结合得更紧密。因此,例如,w . x / y $ z被解析为((w . x) / y) $ z,它是

1
         ($) ((/) ((.) w x) y) z

(函数应用程序本身——您称之为()的应用程序——总是比任何infix运算符绑定得更紧密)。

好的,上面的饮食表达式看起来很混乱,所以我们再来看看加糖的形式,但是仍然使用明确的括号分组:

1
2
3
??〗  ≡ 100 / (fromIntegral (length [1..10]))
??〗  ≡ 100 / (fromIntegral . (length [1..10]))
??〗  ≡ (100 / fromIntegral) $ (length [1..10])

应该立即清楚的是??]不可能是对的,不管操作符实际做了什么:你试图用一个函数来除一个数字,这是没有意义的!$运算符的低优先级专门用于分离表达式——基本上,每当看到这个运算符时,您都可以想象每个操作数周围都有圆括号。

什么??]在解析方面看起来很明智,但在类型方面却没有意义:.不是函数应用程序运算符,而是函数组合运算符。也就是说,它的两个操作数都应该是函数,但(length [1..10])已经是将一个函数应用于它的(唯一)参数的结果,所以您试图用一个数字组成一个函数。

实际上,可以使用composition运算符编写此表达式,但在将函数中的任何一个应用于参数(并且只将参数应用于composition链)之前,必须先组合这些函数:

1
2
3
4
??′〗: 100 / (fromIntegral . length) [1..10]
       ≡ 100 / (\x -> fromIntegral (length x)) [1..10]
       ≡ 100 / fromIntegral (length [1..10])
       ≡ 〖??

至于$,它实际上是一个函数应用程序操作符,所以您也可以使用它。只是,您需要正确使用它的低固定性,并确保它不会干扰高优先级的/运算符:

1
2
3
4
??′〗: 100 / (fromIntegral $ length [1..10])
        ≡ 100 / ((fromIntegral) (length [1..10]))
        ≡ 100 / fromIntegral (length [1..10])
        ≡ 〖??

(.)是一个函数组合运算符,而不是函数应用程序。(.)的优先级定义为9级,功能应用更高。因此,在第二个示例中,首先对Int类型的10进行评估:

1

减少到:

但是(.)(b -> c) -> (a -> b) -> a -> c型。也就是说,Int(a -> b)不匹配。