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

Fof*_*Fof 0 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)
Run Code Online (Sandbox Code Playgroud)

第一个给出了我预期的结果:[([],[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]
Run Code Online (Sandbox Code Playgroud)

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

谢谢,
塞巴斯蒂安.

bhe*_*ilr 5

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

(init . cuts) [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

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

init . cuts [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

它将此解析inits为由函数组成的函数cuts [1, 2, 3],但由于cuts [1, 2, 3]它不是函数,因此会引发类型错误.这是因为函数应用程序总是优先于运算符应用程序.这就是为什么你可以编写类似的表达式

f = zipWith (+) [1..] . filter (< 10) . take 5
Run Code Online (Sandbox Code Playgroud)

代替

f = (zipWith (+) [1..]) . (filter (< 10)) . (take 5)
Run Code Online (Sandbox Code Playgroud)

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

x 1 <#> y
Run Code Online (Sandbox Code Playgroud)

因为它可以被解析为

(x 1) <#> y
Run Code Online (Sandbox Code Playgroud)

要么

x (1 <#> y)
Run Code Online (Sandbox Code Playgroud)

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


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

init . cuts $ [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

用经验法则$ 操作是,它是相同的 具有相同的效果1作为采取任何出现在右边和在括号包裹它,使复杂表达式像

func (func (func (func (func 1))))
Run Code Online (Sandbox Code Playgroud)

可以改为以更好的形式编写

func $ func $ func $ func $ func 1
Run Code Online (Sandbox Code Playgroud)

甚至

func . func . func . func . func $ 1
Run Code Online (Sandbox Code Playgroud)

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

($) :: (a -> b) -> a -> b
f $ x = f x
infixr 0 $
Run Code Online (Sandbox Code Playgroud)

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

  • @bheklilr我仍然认为引入`$`是一种可怕的方式.我看到不止一个人最终认为`$`是一种特殊的语法,用于在parantheses中包含部分表达式. (3认同)