为什么不能通过部分上的方程定义中缀运算符?

Why is it not possible to define an infix operator via an equation on a section?

Hutton 的“Programming in Haskell”第一版指出,串联运算符 ++ 可以定义为:

(++ ys) = foldr (:) ys

这符合逻辑。

我从来没有在其中的一个部分(在本例中 (++ ys))看到一个由方程定义的运算符,所以我自己试了一下:

(@@) :: [a] -> [a] -> [a]
(@@ ys) = foldr (:) ys

然而,这并没有编译,突出了 (@@ ys) 中的语法错误。

这从来都不是一个功能,还是在某个时候被删除了?如果是,为什么?

我知道我可以把上面的写成:

xs @@ ys = foldr (:) ys xs

但我觉得无点样式更优雅。

这会导致一些细微的不一致。尽管我们倾向于认为 curried 和 flipped 以及 uncurried 函数只是编写同一事物的不同方式,但当涉及到实际的评估策略时,这并不完全正确。考虑

(#>) :: Integer -> Integer -> Integer
(#>) n = let p = {- the `n`-th prime number -} `mod` 74
         in (p+)

索引素数的成本很高。如果你写类似

map ((2^43) #>) [100 .. 150]

那么第243个质数只需要计算一次。相比之下,如果我定义

(<#) :: Integer -> Integer -> Integer
(<#) = flip foo

然后写 map (<# (2^43)) [100 .. 150] 会一遍又一遍地计算质数,因为 Haskell 不支持对第二个参数部分应用函数。

根据 flip foo 定义,这并不奇怪,但如果我可以直接将翻转形式定义为

(<#n) = let p = {- the `n`-th prime number -} `mod` 74
        in (p+)

那么可以合理地预期 map (<# (2^43)) 确实 共享素数计算,但要支持 Haskell 的部分评估语义需要跟踪比他们目前提供的信息更多,如果我们希望它可靠地工作,那么它可能会带来一些其他缺点。

我认为对于 = 绑定的 lhs 上允许的句法形式有多复杂,我认为有一个更简单的解释。

请始终 post 您收到的错误消息,不要只是说“突出显示语法错误”。该消息对您来说可能意义不大,但在这种情况下它给出了一个强烈的提示:

(@@ ys) = ...
===> error: Parse error in pattern: @@ys

(xs @@) = ...
===> error: Expression syntax in pattern: xs @@

“有规律”啊哈!也就是说,lhs 可能是一种句法模式。此外,无论您介绍什么,都可能没有签名声明;即使有,编译器也必须根据签名检查你的等式,所以它不能假设你所介绍的内容的真实性。考虑这些有效方程

z         = 42           -- z counts as a pattern
Just z    = {- long and complex expr returning a Maybe, binds z at module-wide scope -}
(Just z)  = {- same same, binds z at module-wide scope -}

foo x     = ...          -- foo gets module-wide scope but not x
(foo x)   = ...          -- same
bar x y   = ...          -- bar gets module-wide scope but not x, y
(bar x) y = ...          -- same

(x ## y) z = ...         -- accepted, introduces triadic operator ##
x ## y z  =              -- rejected error: Parse error in pattern: y
(x ##) y  =              -- rejected error: Expression syntax in pattern: x ##
(## y) z  =              -- rejected error: Parse error in pattern: ##y

语言报告(第 4.4.3 节函数和模式绑定)有

decl   -> (funlhs | pat) rhs
funlhs -> var apat { apat }
        | pat varop pat
        | ( funlhs ) apat { apat }

因此 lhs 不是可以出现表达式语法(包括运算符部分)的地方。另请参阅第 4.4.3.1 节末尾的丑陋细节,将 lhs 运算符语法与中缀数据构造函数结合使用呃!

The last sentence here 还确认您不能在 lhs 上使用运算符部分。