关于haskell:为什么在这个表达式中用括号替换美元符号($)会导致错误?

Why does replacing the dollar sign ($) with parentheses in this expression leads to an error?

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

我有两个表达:

  • foldr (-) 0 . map (uncurry (*)) $ coords 5 7

  • foldr (-) 0 . map (uncurry (*)) (coords 5 7)

  • (1)工作打印出结果,但(2)有错误,表示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <interactive>:50:15:
        Couldn't match expected type ‘a -> t0 c’
                    with actual type[Integer]
        Relevant bindings include
          it :: a -> c (bound at <interactive>:50:1)
        Possible cause:map’ is applied to too many arguments
        In the second argument of(.)’, namely
          ‘map (uncurry (*)) (coords 5 7)
        In the expression: foldr (-) 0 . map (uncurry (*)) (coords 5 7)

    有人能告诉我这两者有什么区别吗?谢谢。


    有一个更简单的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Prelude> id . id $"Example"
    "Example"
    Prelude> id . id ("Example")
    <interactive>:2:10:
        Couldn't match expected type ‘a -> c’ with actual type[Char]
        Relevant bindings include it :: a -> c (bound at <interactive>:2:1)
        In the first argument ofid’, namely ‘("Example")
        In the second argument of(.)’, namely ‘id ("Example")
        In the expression: id . id ("Example")

    问题是函数应用程序的绑定比(.)强。($)的固定水平决定了这一点:

    1
    2
    id . id $"Example" = (id . id) $"Example"
                        = (id . id)"Example"

    但是,使用(...)时,函数应用程序获胜,最终使用(.)时,将非函数作为第二个参数:

    1
    2
    3
    4
    id . id ("Example") = id . id"Example"
                        = id . (id"Example") -- apply id
                        = id . ("Example")
                        = type error, since"Example" isn't a function


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    foldr (-) 0 . map (uncurry (*)) $ coords 5 7
    -- is equivalent to
    ( foldr (-) 0 . map (uncurry (*)) )  (coords 5 7)
    -- and to
    foldr (-) 0 ( map (uncurry (*)) (coords 5 7) )


    foldr (-) 0 . map (uncurry (*)) (coords 5 7)
    -- is equivalent to
    foldr (-) 0 . ( map (uncurry (*)) (coords 5 7) )
    -- and to
    \x -> foldr (-) 0 ( map (uncurry (*)) (coords 5 7) x)

    在后者中,map (uncurry (*)) (coords 5 7)的结果作为第二个参数传递给.,但它是一个列表,而不是一个函数,因此会出现类型错误。

    注意这也可以:

    1
    foldr (-) 0 $ map (uncurry (*)) (coords 5 7)


    $只是一个中缀形式的no-op。因为它是一个固定度很低的中缀运算符:

    1
    2
    3
    GHCi> :i $
    ($) :: (a -> b) -> a -> b   -- Defined in ‘GHC.Base’
    infixr 0 $

    它出现在其中的任何表达式都将被解析为具有括号。在你的例子中,

    1
    foldr (-) 0 . map (uncurry (*)) $ coords 5 7

    被解析为

    1
    2
    3
      (   (foldr (-) 0)
        . (map (uncurry (*))) )
    $ (coords 5 7)

    因为$.固定度低。这与编写1 + 2 * 3的方式完全相同:这被解析为(1) + (2*3),因为*+具有更高的固定性。

    当计算$运算符时,它所做的就是在lhs上应用函数——在您的例子中,这是foldr (-) 0 . map (uncurry (*))——到rhs表达式coords 5 7。当然,将一个函数应用于它的参数也是正确的,如果您刚刚编写了function (argument),那么会发生什么,但是您需要指定正确的函数!要编写不带$的示例,必须将其分组为

    1
    ( foldr (-) 0 . map (uncurry (*)) ) (coords 5 7)

    尽管您的尝试被不同地解析:函数应用程序比任何中缀绑定得更紧密,甚至是.,所以您的尝试相当于

    1
    foldr (-) 0 . ( map (uncurry (*)) (coords 5 7) )

    这不合理。