让函数组合工作不同吗?

Jim*_*ies 1 haskell function-composition

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

reverse(tail(reverse([1,2,3,4])))
Run Code Online (Sandbox Code Playgroud)

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

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]'
Run Code Online (Sandbox Code Playgroud)

我认为这意味着它不喜欢作曲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])'
Run Code Online (Sandbox Code Playgroud)

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

Prelude> let f = reverse . tail . reverse
Prelude> f [1,2,3,4]
[1,2,3]
Run Code Online (Sandbox Code Playgroud)

是什么导致了这个错误,为什么let绑定会阻止这种情况发生?

Sat*_*vik 8

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

Prelude> :i (.)
(.) :: (b -> c) -> (a -> b) -> a -> c   -- Defined in `GHC.Base'
infixr 9 .
Run Code Online (Sandbox Code Playgroud)

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

Prelude> reverse . tail . reverse [1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

Prelude> reverse . tail . (reverse [1,2,3,4])
Run Code Online (Sandbox Code Playgroud)

当您尝试应用合成时,其类型错误.要使合成更正,您必须明确赋予合成优先于函数应用程序的优先级,您可以通过提供明确的括号来完成.

Prelude> (reverse . tail . reverse) [1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

这个解决方案有效,但是黑客们讨厌使用括号.这里有另一个操作符$,它可以在这些地方保存您的代码,并使您的代码比使用括号更具可读性.当你看到的类型$

Prelude> :i ($)
($) :: (a -> b) -> a -> b       -- Defined in `GHC.Base'
infixr 0 $
Run Code Online (Sandbox Code Playgroud)

您可以清楚地看到它的优先级(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]
Run Code Online (Sandbox Code Playgroud)

这是完全正常的,因为第一个操作数$是一个函数而另一个只是一个正确类型的值.