Haskell 中的运算符与匿名函数优先级

cae*_*eus 2 syntax lambda haskell anonymous-function operator-precedence

在 Haskell 中,以下表达式:

 a \b -> c $ d
Run Code Online (Sandbox Code Playgroud)

或者是

• 无效的

(a (\b ->c)) d

(a (\b -> (c d))

为什么?

Jon*_*rdy 7

它在 vanilla Haskell 中是无效的,因为 lambda 不能直接用作函数的参数而没有中间运算符。正确的版本需要使用$或括号:

a   \b -> c $ d
a $ \b -> c $ d
a ( \b -> c $ d )
Run Code Online (Sandbox Code Playgroud)

但是,没有根本原因不允许 lambda(或caseifdo、 或let表达式)出现在那里,因为它没有歧义。所述BlockArguments延伸部(在加入GHC 8.6.1)允许这些语法结构直接作为函数的参数。启用该功能后,它的解析与上述相同,如下所示:

(a (\b -> (c $ d))
Run Code Online (Sandbox Code Playgroud)

这样做的原因不是解析为*(a (\b -> c)) d是一个lambda的范围中定义的Haskell报告§3为尽量向右地(强调)延伸:

关于lambda 抽象、let 表达式和条件的范围,语法是不明确。歧义由元规则解决,即这些结构中的每一个都尽可能向右延伸。

换句话说,可以认为 lambda 的主体比任何其他表达式具有更低的优先级。这个符号直接从 lambda 演算借用,其中a ? c d与 ( a (? b . ( c d ))) 相同。

请注意,我没有删除$此处:这两个表达式是不同的,即使它们的计算结果相同:

f x
f $ x
Run Code Online (Sandbox Code Playgroud)

第一个是函数f对参数的应用x;第二个是运算符($)对参数fand 的应用x。像所有中缀运算符一样,它是普通前缀函数调用的语法糖:

($) f x
Run Code Online (Sandbox Code Playgroud)

($)被定义为具有最低运算符优先级的右结合运算符(即x $ y $ z= x $ (y $ z)而不是* (x $ y) $ z),声明infixr 0Prelude. 您可以在 GHCi 中使用:info(或:i) 命令查看有关运算符的信息:

> :info $
($) :: (a -> b) -> a -> b   -- Defined in ‘GHC.Base’
infixr 0 $
Run Code Online (Sandbox Code Playgroud)