Loading...

简洁即力量

Original

May 2002

"代数符号将大量意义压缩到很小的空间中,这是另一种有助于我们习惯于借助它们进行推理的情况。"

  • 查尔斯·巴贝奇,引自艾弗森的图灵奖演讲

在关于 Revenge of the Nerds 在 LL1 邮件列表中引发的议题的讨论中,保罗·普雷斯科德写了一些让我印象深刻的话。

Python 的目标是规律性和可读性,而不是简洁性。

表面上看,这似乎是对一门编程语言的相当严厉的评价。据我所知,简洁性 = 力量。 如果是这样,那么代入后,我们得到

Python 的目标是规律性和可读性,而不是力量。

这似乎不是你想要做出的权衡(如果它 权衡)。 这几乎等同于说 Python 的目标不是成为一门有效的编程语言。

简洁性 = 力量吗?这在我看来是一个重要的问题, 也许是任何对语言设计感兴趣的人最重要的一个问题,也是一个值得直接面对的问题。我还没有确定答案是否是一个简单的“是”,但它似乎是一个很好的假设,可以作为起点。

假设

我的假设是简洁性就是力量,或者至少足够接近, 以至于除了病态的例子之外,你可以将它们视为 相同的。

在我看来,简洁性是编程语言的 目的。计算机同样乐意直接用机器语言被告知该做什么。我认为我们费尽心思开发高级语言的主要 原因是为了获得杠杆作用,这样我们就可以用 10 行高级语言来表达用 1000 行机器语言才能表达的内容。换句话说, 高级语言的主要意义在于使源代码更小。

如果更小的源代码是高级语言的目的, 而事物的强大程度取决于它实现其目的的程度,那么 衡量编程语言强大程度的标准是它使你的程序变得多小。

反之,一种不能使你的程序变小的语言 在编程语言应该做的事情上做得不好,就像一把不能很好地切割的刀,或者印刷模糊不清的文字。

指标

不过,小在什么意义上呢?代码大小最常见的衡量标准是 代码行数。但我认为这个指标之所以最常见,是因为 它最容易测量。我不认为有人真正相信 它是衡量程序长度的真正标准。不同的 语言对应该在一行上放多少内容有不同的约定;在 C 中,很多行除了分隔符之外什么都没有。

另一个简单的测试是程序中的字符数,但这也不太好;有些语言(例如 Perl) 只是 使用比其他语言更短的标识符。

我认为衡量程序大小的更好指标是 元素数量,其中元素是任何在绘制表示 源代码的树时将成为一个独立节点的东西。 变量或函数的名称是一个元素; 一个整数或一个浮点数是一个元素; 一段文字是一个元素; 模式中的一个元素,或一个格式指令,是一个元素; 一个新的块是一个元素。有一些边界情况 (-5 是两个元素还是一个?)但我认为大多数情况对 每种语言都是一样的,所以它们对比较的影响不大。

这个指标需要进一步完善, 并且在特定语言的情况下可能需要解释, 但我认为它试图衡量正确的东西,即 程序的组成部分数量。我认为你在这个 练习中绘制的树是你为了 理解程序而必须在脑海中构建的,因此它的规模与 你编写或阅读它所需要的工作量成正比。

设计

这种指标可以让我们比较不同的语言, 但这对我来说并不是它的主要价值。简洁性测试的主要价值 在于作为 设计 语言的指南。 语言之间最有用的比较是两种 相同语言的潜在变体之间的比较。我可以在语言中做些什么来使程序更短?

如果程序的概念负载与它的复杂性成正比,而一个给定的程序员 可以容忍固定的概念负载,那么这等同于问, 我该怎么做才能让程序员完成更多工作?而且 在我看来,这与问如何设计一门好的语言 是相同的。

(顺便说一句,没有什么比设计 语言更能明显地证明“所有语言都是等价的”这个老生常谈是错误的。当你设计一门新语言时,你 不断地 比较两种语言——如果我做了 x,以及如果我没有做——来 决定哪种更好。如果这真的是一个毫无意义的问题, 你不如抛硬币。)

追求简洁性似乎是找到新想法的好方法。 如果你能做一些让许多 不同的程序更短的事情,这可能不是巧合:你可能 发现了一个有用的新抽象。你甚至可能 能够编写一个程序来帮助你通过搜索 源代码中的重复模式来实现。在其他语言中,那些 以简洁性著称的语言将是寻找新想法的最佳选择:Forth、Joy、Icon。

比较

据我所知,第一个写到这些问题的人是 弗雷德·布鲁克斯在 人月神话 中。他写道 程序员似乎每天生成大约相同数量的代码,无论使用哪种语言。 当我二十出头第一次读到这句话时, 我感到非常惊讶,而且似乎有着巨大的影响。 这意味着 (a) 唯一能更快地编写软件的方法是 使用更简洁的语言,以及 (b) 任何花时间这样做的人都可以将那些没有这样做的人远远地甩在身后。

布鲁克斯的假设,如果它是真的,似乎是黑客精神的核心。 在过去的几年里,我一直密切关注我所能获得的关于这个问题的任何证据,从正式研究到关于个人 项目的轶事。我还没有看到任何与他相矛盾的东西。

我还没有看到在我看来具有决定性的证据, 而且我不指望看到。像卢茨·普雷切尔特对编程语言的比较这样的研究,虽然 产生了与我预期相符的结果,但往往使用的问题太短,无法成为有意义的测试。对一门语言的更好测试是 在需要一个月才能写完的程序中会发生什么。而且唯一的 真正测试,如果你像我一样相信语言的主要目的是便于思考(而不仅仅是在你想到之后告诉计算机该做什么),那就是你用它能写出什么新东西。 因此,任何语言比较, 如果你必须满足一个预先定义的规范,那么它就是在测试一些错误的东西。

衡量一门语言的真正标准是你能用它发现和解决新问题的程度,而不是 你用它来解决别人已经提出的问题的程度。这两者是截然不同的标准。 在艺术中,刺绣和马赛克等媒介,如果你 事先知道你想做什么,那么它们很有效,但如果你 不知道,那么它们就完全糟糕透顶。当你想要在创作过程中发现图像时—— 就像你必须对像人像这样复杂的东西做的那样——你需要使用更流畅的媒介,比如铅笔、墨水或油画。事实上,挂毯和马赛克的制作方式是 先画一幅画,然后复制它。(“卡通”这个词最初指的是为 这个目的而绘制的画。)

这意味着我们不太可能对编程语言的相对力量进行准确的比较。我们会进行精确的 比较,但不是准确的比较。特别是,为了比较语言而进行的明确研究, 因为它们可能会使用小问题,并且必然会使用 预先定义的问题,因此往往会低估更强大语言的力量。

来自该领域的报告,虽然它们必然不如 “科学”研究那样精确,但更有可能更有意义。例如, 爱立信的乌尔夫·维格尔做了一个 研究, 得出结论,Erlang 比 C++ 简洁 4-10 倍,并且开发软件的速度也相应地更快:

爱立信内部开发项目之间的比较表明, 包括软件开发的所有阶段在内,每小时的代码行数生产率相似, 与使用哪种语言(Erlang、PLEX、C、C++ 或 Java)无关。那么,区分不同语言的因素就变成了源代码量。

该研究还明确地处理了布鲁克斯的书中 只隐含的一个观点(因为他测量的是调试后的代码行数): 用更强大的语言编写的程序往往有更少的错误。 这本身就成为一个目标,可能比程序员 生产力更重要,尤其是在网络交换机等应用中。

品尝测试

最终,我认为你必须相信你的直觉。用这门语言编程感觉如何?我认为找到(或设计) 最佳语言的方法是变得对一门语言让你思考的程度高度敏感,然后选择/设计感觉最好的语言。如果 某些语言特性很笨拙或有局限性,不要担心,你会 知道的。

这种高度敏感性会付出代价。你会发现你无法 忍受 用笨拙的语言编程。我发现用没有宏的语言编程非常受限制,就像一个习惯了动态类型的人发现必须回到 用必须声明每个变量的类型,并且不能创建不同类型对象的列表的语言编程非常受限制一样。

我不是唯一一个。我知道很多 Lisp 黑客也遇到了这种情况。事实上,衡量编程语言相对力量最准确的标准可能是,知道这门语言的人中,有多少人会接受任何能用这门语言工作的职位,无论应用领域如何。

限制性

我认为大多数黑客都知道一门语言感觉受限意味着什么。 当你感觉到这种感觉时,到底发生了什么?我认为这与 你想走的街道被封锁了,你必须绕很远的路才能到达你想去的地方的感觉一样。你想要说些什么,而语言不允许你。

我认为真正发生的事情是,一门限制性的语言 不够简洁。问题不仅仅是你不能 说出你打算说的话。而是语言让你走的弯路 更长。尝试一下这个思维实验。假设有一些 你想要编写的程序,而语言不允许你 用你计划的方式表达它,而是强迫你用其他方式编写程序,而这种方式 更短。至少对我来说, 这不会让人感觉很受限制。这就像你想走的街道被封锁了,而路口处的警察却指引你走捷径而不是绕路。太好了!

我认为大多数(百分之九十?) 受限的感觉来自于被迫使你编写的程序 比你脑海中的程序更长。限制性主要是缺乏简洁性。 因此,当一门语言感觉受限时,这意味着它(主要是)不够简洁,而当一门语言不够简洁时,它就会 让人感觉受限。

可读性

我开头引用的那句话提到了另外两个品质,规律性和 可读性。我不确定规律性是什么,或者代码具有规律性和可读性比仅仅 具有可读性有什么优势,如果有的话。但我认为我知道可读性指的是什么,而且我认为 它也与简洁性有关。

我们必须小心地区分 单个代码行的可读性和整个程序的可读性。 重要的是后者。我同意 Basic 的一行代码可能比 Lisp 的一行代码更易读。但是用 Basic 编写的程序 将比用 Lisp 编写的相同程序有更多的行(尤其是当你进入 Greenspunland 之后)。阅读 Basic 程序的 总工作量肯定会更大。

总工作量 = 每行工作量 x 行数

我不确定可读性是否与简洁性直接成正比, 就像力量一样,但简洁性肯定是一个因素 (在数学意义上;见上式)在可读性中。 因此,说一门语言的目标是可读性,而不是简洁性,可能没有意义;这就像说目标是可读性, 而不是可读性一样。

对于第一次接触这门语言的用户来说,每行可读性意味着源代码将 看起来没有威胁性。所以 每行可读性 可能是一个好的营销决策,即使它是一个糟糕的设计 决策。它与非常成功的分期付款技术同构:与其用高昂的前期价格吓唬他们,不如告诉他们低廉的月供。分期付款 对买方来说是一个净损失,就像每行可读性对程序员来说可能 是一个净损失一样。 买方将支付 很多 这些低廉的月供;而 程序员将阅读 很多 这些单独可读的行。

这种权衡早于编程语言。如果你习惯于阅读 小说和报纸文章,那么你第一次阅读数学 论文的经历可能会令人沮丧。阅读一页可能需要半小时。 然而,我相当肯定符号不是问题,即使 它可能感觉像是问题。数学论文很难读 是因为里面的想法很难。如果你用散文来表达相同的想法 (就像数学家在他们发展出简洁的符号之前必须做的那样), 它们不会更容易阅读,因为论文会膨胀到 一本书的大小。

到什么程度?

许多人拒绝 简洁性 = 力量的观点。我认为,与其简单地争论它们是否相同,不如问: 简洁性 = 力量到什么 程度? 因为显然简洁性是 高级语言存在的很大一部分原因。如果它不是它们存在的全部原因,那么它们还存在什么其他原因,以及这些 其他功能的相对重要性如何?

我提出这个问题不仅仅是为了让辩论更文明。我真的很 想知道答案。什么时候,如果有的話,一门语言会因为过于简洁而 自食其果?

我开始时的假设是,除了病态的例子之外, 我认为简洁性可以被认为与力量相同。我的意思是,在任何人们会设计的语言中,它们 都是相同的,但如果有人想设计一门语言 专门为了反驳这个假设,他们可能就能做到。实际上,我甚至不确定这一点。

语言,而不是程序

我们应该明确的是,我们讨论的是 语言的简洁性,而不是单个程序的简洁性。 单个程序当然有可能被写得过于密集。

我在 On Lisp 中写过这个问题。一个复杂的宏 可能必须节省其自身长度的很多倍才能证明其合理性。如果你写 一些复杂的宏可以让你每次使用它时节省十行代码, 而宏本身是十行代码,那么如果你使用它超过一次,你就能获得代码行的净节省。但这仍然可能是一个糟糕的举动, 因为宏定义比普通代码更难读。你 可能必须使用宏十次或二十次才能获得可读性的净改进。

我相信每种语言都有这样的权衡(虽然我怀疑 随着语言变得越来越强大,风险也会越来越高)。每个程序员都必须 见过一些聪明人用可疑的编程技巧使代码略微缩短的代码。

所以,关于这一点没有争议——至少,我这边没有。单个 程序当然有可能过于简洁而自食其果。问题是,一门语言可以吗?一门语言可以强迫程序员编写 代码,使其在(元素)方面很短,但以整体可读性为代价吗?

很难想象一门语言会过于简洁的一个原因是,如果 存在某种过于紧凑的表达方式,那么可能也存在一种更长的表达方式。例如,如果你觉得使用 大量宏或高阶函数的 Lisp 程序过于密集,你可以,如果你 愿意,编写与 Pascal 同构的代码。如果你 不想用 Arc 中对高阶函数的调用来表达阶乘


(rec zero 1 * 1-)

你也可以写出一个递归定义:


(rfn fact (x) (if (zero x) 1 (* x (fact (1- x)))))

虽然我一时想不出任何例子,但我对这个问题很感兴趣,即一门语言是否可能过于简洁。是否存在语言 强迫你以一种笨拙且难以理解的方式编写代码? 如果有人有例子,我很想看看。

(提醒:我想要的是根据上面概述的“元素”指标非常密集的程序,而不仅仅是那些 因为可以省略分隔符并且所有内容都只有一个字符的名称而很短的程序。)