让函数组合工作不同吗?

时间:2013-01-09 12:53:41

标签: haskell function-composition

要从GHCi中的列表中删除最后一项,我可以反转列表,取尾然后再将其反转。例如,

reverse(tail(reverse([1,2,3,4])))

由于那里有很多括号,我以为我会改变它来使用功能组合。但是,当我尝试这个时,我得到以下错误。

Prelude> reverse . tail. reverse [1,2,3,4]

<interactive>:2:17:
    Couldn't match expected type `a0 -> [a1]' with actual type `[a2]'
    In the return type of a call of `reverse'
    Probable cause: `reverse' is applied to too many arguments
    In the second argument of `(.)', namely `reverse [1, 2, 3, 4]'
    In the second argument of `(.)', namely
      `tail . reverse [1, 2, 3, 4]'

我认为这意味着它不喜欢编写reverse [1,2,3,4],所以我尝试在它周围加上括号,但它给了我同样的错误。

Prelude> reverse . tail. (reverse [1,2,3,4])

<interactive>:3:18:
    Couldn't match expected type `a0 -> [a1]' with actual type `[a2]'
    In the return type of a call of `reverse'
    Probable cause: `reverse' is applied to too many arguments
    In the second argument of `(.)', namely `(reverse [1, 2, 3, 4])'
    In the second argument of `(.)', namely
      `tail . (reverse [1, 2, 3, 4])'

但是,如果我执行以下操作,它可以正常工作。

Prelude> let f = reverse . tail . reverse
Prelude> f [1,2,3,4]
[1,2,3]

导致此错误的原因是什么?为什么let绑定会阻止此错误发生?

2 个答案:

答案 0 :(得分:8)

当你看到.的类型时,你会注意到它的两个操作数都是函数(合适的类型,以便它们可以组合)

Prelude> :i (.)
(.) :: (b -> c) -> (a -> b) -> a -> c   -- Defined in `GHC.Base'
infixr 9 .

现在,函数应用程序的优先级高于任何中缀运算符,所以

Prelude> reverse . tail . reverse [1,2,3,4]

成为

Prelude> reverse . tail . (reverse [1,2,3,4])

当您尝试应用合成时,其类型错误。要使组合更正,你必须明确给出比函数应用程序更高的优先级,你可以通过提供明确的括号来完成。

Prelude> (reverse . tail . reverse) [1,2,3,4]

此解决方案有效,但是黑客们讨厌使用括号。这里有另一个运算符$,它可以将您保存在这些位置,并使您的代码比使用括号更具可读性。 当您看到$

的类型时
Prelude> :i ($)
($) :: (a -> b) -> a -> b       -- Defined in `GHC.Base'
infixr 0 $

您可以清楚地看到它的优先级(0)小于构图(9)的优先级,它可以为您提供所需的效果。您只需降低上一个函数应用程序的优先级或用$替换函数组合。

Prelude> reverse . tail . reverse $ [1,2,3,4]
Prelude> reverse . tail $ reverse [1,2,3,4]
Prelude> reverse $ tail $ reverse [1,2,3,4]

这很完美,因为$的第一个操作数是一个函数而另一个只是一个正确类型的值。

答案 1 :(得分:4)

let情况下,函数由.组成,然后列表作为函数组合结果的参数:

(reverse . tail . reverse) [1,2,3,4]

在另一种情况下,列表作为reverse的参数给出,并且其结果试图与其他一些函数组合:

reverse . tail . (reverse [1,2,3,4])

由于reverse [1,2,3,4]返回的反向列表不是函数,因此将其用作.的参数会导致类型错误。