将: 放在括号中使您的第二个示例有效,而不是在flip 周围使用反引号。
我们经常说“函数应用具有最高优先级”来强调,例如f x + 1 应该读作(f x) + 1,而不是f (x + 1)。但这并不完全准确。如果是这样,并且(flip :) 已按您的预期解析,那么在(f x) + 1 之后的最高优先级将是(f x) 对+ 的应用;整个表达式 f x + 1 最终会被解析为 f 应用于 3 个参数:x、+ 和 1。但这会发生在所有涉及中缀运算符的表达式中!即使是简单的1 + 1 也会被识别为1 应用于+ 和1(然后抱怨缺少Num 实例,这将使1 成为一个函数)。
本质上,对“函数应用程序具有最高优先级”的这种严格理解意味着函数应用程序将是所有发生的事情;中缀运算符总是作为某个函数的参数结束,而实际上从不作为中缀运算符。
实际上,优先级(和关联性)是解决涉及多个中缀运算符的表达式的歧义的机制。函数应用程序不是中缀运算符,并且根本不参与优先/关联系统。不涉及运算符的术语链被解析为函数应用程序 before 优先级被调用来解析运算符应用程序(因此“最高优先级”),但它不是 真正 优先级导致它。
这是它的工作原理。您从术语和运算符的线性序列开始;没有结构,它们只是挨着写。
我在这里所说的“术语”可以是非运营商标识符,例如flip;或字符串、字符或数字文字;或列表表达式;或带括号的子表达式;等等。就这个过程而言,它们都是不透明的;我们只知道(并且只需要知道)它们不是中缀运算符。我们总是可以告诉运算符,因为它要么是像++!@> 这样的“符号”标识符,要么是反引号中的字母数字标识符。
因此,术语和运算符的序列。您会在一行中找到一个或多个术语的所有链,其中不包含任何运算符。每个这样的链都是一个功能应用链,并成为一个单独的术语。1
现在,如果您有两个直接相邻的运算符,则会出现错误。如果您的序列以运算符开始或结束,这也是一个错误(除非这是一个运算符部分)。
此时,您可以保证有一个严格交替的序列,例如 term operator term operator term operator term 等。因此,您选择具有最高优先级的运算符及其左右两侧的术语,称其为运算符应用程序,这三个项目成为一个术语。当您有多个具有相同优先级的运算符时,关联性充当平局。冲洗并重复,直到整个表达式变成一个术语(或关联性无法打破平局,这也是一个错误)。这意味着在涉及运算符的表达式中,“顶级应用程序”始终是运算符之一,而不是普通的函数应用程序。
这样做的结果是,在没有任何情况,运算符最终可以作为参数传递给函数。这简直是不可能的。这就是为什么我们需要(:) 语法来禁用运算符的“运算符性”,并将它们的身份作为值来获取。
对于flip :,唯一的非运算符术语链就是flip,因此没有普通的函数应用程序可以“以最高优先级”解析。 : 然后去寻找它的左和右参数(但这是一个部分,所以没有右参数),并在它的左边找到flip。
要使flip 接收: 作为参数而不是相反,您必须编写flip (:)。 (:) 不是运算符(它在括号中,所以里面的内容无关紧要),所以我们有一个没有运算符的两个术语链,因此通过将 flip 应用于 @ 可以解析为单个表达式987654353@.
1 另一种看待这个问题的方法是,您识别所有未被运算符分隔的术语序列,并在它们之间插入“函数应用运算符”。此“运算符”的优先级高于分配给其他运算符的优先级,并且是左关联的。然后,运算符解析逻辑将自动按照我描述的方式处理函数应用程序。