Haskell 函数组合在let中的工作方式是否不同?

Haskell 函数组合在let中的工作方式是否不同?,haskell,function-composition,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 actua

要从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]'
但如果我做了以下操作,它就会正常工作

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

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

let
情况下,函数由
组成,然后给出一个列表作为函数组成结果的参数:

(reverse . tail . reverse) [1,2,3,4]
在另一种情况下,列表作为参数提供给
reverse
,其结果尝试与一些其他函数组合:

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

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

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

(reverse . tail . reverse) [1,2,3,4]
在另一种情况下,列表作为参数提供给
reverse
,其结果尝试与一些其他函数组合:

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

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

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

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

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

Prelude> reverse . tail . (reverse [1,2,3,4])
当你尝试应用构图时,它的类型是错误的。为了使合成正确,您必须显式地赋予合成比函数应用程序更高的优先级,这可以通过提供显式括号来实现

Prelude> (reverse . tail . reverse) [1,2,3,4]
这个解决方案是可行的,但Haskeller不喜欢使用括号。下面是另一个操作符
$
,它可以将您保存在这些位置,并使您的代码比使用括号更可读。 当您看到
$

Prelude> :i ($)
($) :: (a -> b) -> a -> b       -- Defined in `GHC.Base'
infixr 0 $
你可以清楚地看到,它的频率(0)比堆肥(9)的频率(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]

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

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

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

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

Prelude> reverse . tail . (reverse [1,2,3,4])
当你尝试应用构图时,它的类型是错误的。为了使合成正确,您必须显式地赋予合成比函数应用程序更高的优先级,这可以通过提供显式括号来实现

Prelude> (reverse . tail . reverse) [1,2,3,4]
这个解决方案是可行的,但Haskeller不喜欢使用括号。下面是另一个操作符
$
,它可以将您保存在这些位置,并使您的代码比使用括号更可读。 当您看到
$

Prelude> :i ($)
($) :: (a -> b) -> a -> b       -- Defined in `GHC.Base'
infixr 0 $
你可以清楚地看到,它的频率(0)比堆肥(9)的频率(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]

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

只是说,init function完成了这项工作。@zurgl我知道,这只是我遇到的问题的一个例子;)我要说的是,init函数完成了这项工作。@zurgl我知道,这只是我遇到的问题的一个例子;)