为什么评估`init的结果。 cut [1,2,3]`与`(init.cutters)[1,2,3]不同?

时间:2014-05-06 17:11:34

标签: haskell types unification

我试图理解为什么这两个评估:(init . cuts) [1,2,3]init . cuts [1,2,3]不同,其中:

cuts :: [a] -> [([a],[a])]
cuts xs = zipWith splitAt [0..length xs] (repeat xs)

第一个给出了我预期的结果:[([],[1,2,3]),([1],[2,3]),([1,2],[3])] ,但第二个返回此错误:

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

我认为init . cuts [1,2,3] = init (cuts[1,2,3]),这是正确的吗?

谢谢,
安。

1 个答案:

答案 0 :(得分:5)

这是因为固定性或操作顺序。当编译器看到

(init . cuts) [1, 2, 3]

它将此解析为带有参数inits . cuts的函数[1, 2, 3]。当它看到

init . cuts [1, 2, 3]

它将此解析为由函数inits组成的函数cuts [1, 2, 3],但由于cuts [1, 2, 3]不是函数,因此会引发类型错误。发生这种情况是因为函数应用程序始终优先于运算符应用程序。这就是你可以编写像

这样的表达式的原因
f = zipWith (+) [1..] . filter (< 10) . take 5

而不是

f = (zipWith (+) [1..]) . (filter (< 10)) . (take 5)

如果运算符优先级可能高于函数应用程序优先级,那么您可能必须根据运算符优先级使用后一种形式,然后表达式变得更难以使编译器和程序员解析。这可能意味着可能存在类似

之类的歧义
x 1 <#> y

因为它可以被解析为

(x 1) <#> y

x (1 <#> y)

取决于优先级(我只是组成了一个运算符<#>)。


正如@chepner正确地指出的那样,如果你想避免使用括号(就像许多Haskeller那样),你可以使用$运算符,它有一个精心选择的固定性来允许你做

init . cuts $ [1, 2, 3]

$ 运算符的经验法则是是相同的 具有相同的效果 1 < / sup>采取右边的任何内容并将其包装在括号中,因此需要复杂的表达式,如

func (func (func (func (func 1))))

可以用更好的形式写成

func $ func $ func $ func $ func 1

甚至

func . func . func . func . func $ 1

1 正如@Cubic所指出的,有些人可能会认为$是Haskell的一些特殊语法。不是这种情况。它被简单地定义为

($) :: (a -> b) -> a -> b
f $ x = f x
infixr 0 $

实际上,这个函数与id函数相同,只是表示为与右边相关联的优先级非常低的中缀运算符,而这种固定性使得运算符非常干净地表达代码。它可以等效于使用更多括号,但这并不能使它成为替代语法。这只是另一个功能。