Syntax with Purpose in a Programming Language

Lobsters Hottest 新闻

摘要

这篇文章探讨了编程语言语法设计的重要性,认为语法应准确反映语言的计算模型和心智模型,而非为了熟悉感或简洁性随意拼凑。作者通过分析OCaml、Lisp/Clojure和JavaScript的语法设计,并介绍自己设计的语言Saul,强调了统一性和语义一致性。

<p><a href="https://lobste.rs/s/bovmc5/syntax_with_purpose_programming">Comments</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/13 13:53

TL;DR: 编程语言的语法不是随意的,它应该准确反映语言底层的计算模型和心智模型,而不是为了“熟悉”或“省字”而胡乱拼凑。 ## 语法的真实作用 许多人把编程语言的语法当作一堆为了少打字或“提供表现力”而随意塞进去的东西——比如 Python 的《The Zen of Python》说“Write less”。但这毫无保证,语法设计的好坏直接决定了语言能否清晰表达意图。我正将一个 C++ 数据库内核重写为 OCaml,在这个过程中深刻体会到语法对心智模型的影响。 ## OCaml 中的 `let in`:嵌套与作用域 在 OCaml(以及 ML 家族的其它语言)里,`let in` 是一个表达式:你先给一个变量取个名字,再给它一个值,然后 `in` 后面的表达式会在这个绑定下求值。这跟 C 语言里的变量赋值不一样。C 里的变量更像“内存盒子的名字”,而 `let in` 则更接近 lambda 演算中的替换——没有赋值,只有将名字替换成对应的表达式。 ```ocaml let x = 1 in let y = x + 2 in y * 3 ``` 你可以像嵌套函数一样嵌套这些绑定,它本质上是柯里化的。关键是 **语法要告诉你正在做什么**。在模块层级,OCaml 也用 `let` 但没有 `in`,因为求值的终点是模块而不是下一个内部表达式。而在函数内部,你又可以看到 `in` 来分隔不同的作用域。这种语法上的细微差异,恰恰反映了语言语义上的不同——前者是“在模块范围内定义”,后者是“在表达式局部绑定并继续求值”。 ## Lisp 与 Clojure:一致的列表与向量语法 Lisp 里用 `let` 做局部绑定: ```elisp (let ((b 2)) (+ a b)) ``` 这里看到一个列表的列表:外层的 `let` 表达式是一个列表,其第一个子列表是绑定列表,每个绑定又是一个两元素列表(变量名和值)。结构非常一致:你只需要理解“列表”这一种数据结构,然后 `let` 特意按照特定的方式解释它。 Clojure 则用向量来实现同样的效果: ```clojure (let [b 2] (+ a b)) ``` 这里用了 `[b 2]` 这样的向量,不再是列表的列表。但阅读器(reader)按相同方式解读:成对地取元素作为(值,标签)。实际上 Clojure 也允许用 map 形式(`{key value}`)来做绑定,只是加上逗号后读取器会忽略逗号。无论哪种写法,核心思想是一样的:**语言提供一个统一的元素(如列表、向量、map),然后在不同上下文(如 `let`、`fn`、`cond`)中用同一套规则去解析它**。这种一致性减少了认知负担。 ## JavaScript 的语法灾难 JavaScript 的语法是绝对糟糕的。它本应是一个 Scheme(一种交互式、类 Lisp 的语言),但因为 90 年代 Java 和 C 流行,为了“熟悉感”硬生生改成了类 C 的语法。结果就是,它试图表示它本不是的东西。这种“熟悉”只是“更糟就是更好”的论点。JavaScript 的语法没有统一的底层语义支撑,而是充满了历史遗留的特殊规则和特殊情况。 ## 我的语言 Saul:统一性设计 我正在写一门叫 Saul 的语言,它是一个不完全的图灵完备语言,保证全函数性质(所有函数都会终止)。这在数据库设计中很重要,因为像 PL/pgSQL 这样允许任意递归的语言是一支巨大的脚枪。Saul 的语法灵感来自 Prolog 和 Smalltalk,力求统一。 例如关系(relation)的写法: ```saul integer(42) % 42 与 integer 关系关联 person("Alice") % 字符串绑定到 person plus(1, 2, 3) % plus 关系,类似函数 ``` 想法是:**整个语言的语法只有一种核心构造——关系**。变量、函数、类型都在这个框架下统一表达。这比 OCaml 那种“为了简洁抛弃统一性”的做法更好。OCaml 最初面向 .NET,后来成为独立语言,已经把自己的语法弄得支离破碎:你仍然可以输入某些古老的关键字,但格式化程序会去掉它们,结果看起来像你只是在做一种松散的双重绑定,混淆了两类本质上不同的东西。 ## 结论:语法设计不是随意的 语法的规范必须服务于语言的心智模型。当你在一门语言中写代码时,你处于一种特定的思维模式。Lisp 好的地方在于你可以随时改变处理问题的方式(宏),而对大多数语言来说,你只能使用语言提供的有限抽象。这些抽象背后应该有一个动机,而不是为了证明某个特殊规则或者让新手“感觉熟悉”而随手创造。如果语法不能清晰地告诉你每个语句代表什么,不能表达求值项在封闭世界假设下的操作,那它就是糟糕的设计。 这绝不是琐碎的抱怨。虽然它真的让我很抓狂。 Source: [https://www.youtube.com/watch?v=_HLZoeFREFo](https://www.youtube.com/watch?v=_HLZoeFREFo)

相似文章

Stroustrup法则(2024)

Hacker News Top

Bjarne Stroustrup的法则指出:对于新特性,程序员更偏好显式语法,但一旦该特性被确立,他们就更倾向于简洁表示法。本文探讨了 Rust 和 Python 中的示例,并讨论了这对语言设计和教学的影响。

无语义的语法:教会大语言模型用未见过的语言编程

arXiv cs.CL

本文介绍了PyLang,一种在所有预训练语料库中都不存在的编程语言,并表明在其上微调的大语言模型可以学习语法但无法迁移算法推理,导致出现“实现忠实度差距”——模型理解算法但无法用不熟悉的语言表达它们。

迈向可理解的软件

Lobsters Hottest

本文批判了当前的编程实践和对大语言模型的依赖,反而主张通过更好的抽象、文档和软件栈来使代码更易于理解和维护。