我们需要谈谈括号(2020)

围绕编程语言在多大程度上应依赖括号和运算符优先级的争论,揭示了可读性、熟悉度与元编程能力之间的深层权衡。一些参与者主张尽量减少优先级规则、更多显式分组(甚至采用 RPN 风格语法),而另一些人则为传统的类数学规则和 Lisp 那种完全括号化的 S 表达式辩护,尤其是在配合良好的缩进和编辑器支持时。讨论还涉及语法如何影响工具、作用域以及代码转换的便利性,以及为什么尽管 Lisp 具有宏和“代码即数据”的优势,大多数主流语言仍然倾向于更丰富、也更不统一的语法形式。

运算符优先级 vs. 强制括号

  • 一派认为运算符优先级是一个设计错误;像 1 + 1 * 2a | b || c >> d & e * f 这样的混合运算表达式,除非显式加括号,否则都应该是语法错误。
  • 另一派强烈反对,理由包括记号更简洁、与代数类比,以及过度加括号代码带来的负担。
  • 提议的折中方案是:采用一种 部分 优先级顺序。常见组合(例如 * 高于 +)允许;不常见的混合(例如 *&)则以“优先级歧义”为由拒绝。
  • 争论还延伸到数学:有人认为优先级是内在的(“运算顺序”);也有人坚持它纯粹是记号的属性,往往通过分数、上标、根号等视觉方式来表达。

Lisp 括号、可读性与工具

  • 许多人指出,在 Lisp 中,自动缩进、结构化编辑和括号高亮被视为必不可少;这些工具能迅速暴露缺失或放错位置的括号。
  • 有人认为 Lisp 只有一种括号会损害可读性,并建议为不同的语法角色使用多种括号类型,甚至让不同括号仅用于视觉分组、彼此可互换。
  • 反方观点是:有经验的 Lisp 用户更多是“看形式”,而不是看括号,他们依赖以符号开头的模式,而不是数括号。
  • 结构化编辑受到赞扬:显式分隔符加自动格式化,像代码结构的“复式记账”。

语法风格、空白与作用域

  • 有人称赞那些在 if 条件等结构中去掉不必要括号的语言(Go、Scala 3)。
  • 对显著空白也存在分歧:有些人偏好显式分隔符;另一些人则觉得以缩进为中心的语法更易读。
  • 讨论还涉及作用域:紧凑的词法块({}let 形式)可以减少活动变量,但并非所有生命周期都能被最小化;C、Rust(非词法生命周期)以及函数式/单子代码中的例子都说明了这些权衡。

Lisp 的代码即数据与元编程

  • 一些评论强调,Lisp 的括号表示的是列表;程序就是列表,而引用则把代码变成数据。
  • 简单的列表操作就能转换代码(例如把所有 * 替换成 + 然后 eval),这为强大的宏和领域特定语法提供了基础。
  • 支持者认为这是 Lisp 的核心优势;怀疑者则回应说,如今其他语言也提供了强大的元编程能力,同时视觉上不那么重复,并质疑括号是否真的是更好的人体工学折中。