我试图理解为什么这两个评估:(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])
,这是正确的吗?
谢谢,
安。
答案 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
函数相同,只是表示为与右边相关联的优先级非常低的中缀运算符,而这种固定性使得运算符非常干净地表达代码。它可以等效于使用更多括号,但这并不能使它成为替代语法。这只是另一个功能。