Loading...

简洁就是力量

Original

2002 年 5 月

“代数符号将大量含义压缩到狭小的空间中,这是另一种有助于我们借助其进行推理的情况。”

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

在 LL1 邮件列表上讨论《书呆子的复仇》所提出的问题的时,Paul Prescod 写了一些让我印象深刻的东西。

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

从表面上看,这似乎是对编程语言的相当不利的说法。据我所知,简洁=强大。如果是这样,那么代入后,我们得到

Python 的目标是规律性和可读性,而不是强大。

这似乎不是您想要做出的权衡(如果这权衡的话)。这与说 Python 的目标不是成为一种有效的编程语言相差无几。

简洁性 = 力量吗?在我看来,这是一个重要的问题,也许是对任何对语言设计感兴趣的人来说最重要的问题,也是直接面对的问题。我还不确定答案是否是简单的肯定,但这似乎是一个很好的假设。

假设

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

在我看来,简洁就是编程语言*的用途。*计算机同样乐意用机器语言直接告诉它该做什么。我认为我们费尽心思开发高级语言的主要原因是为了获得优势,这样我们就可以用 10 行高级语言说出(更重要的是,思考)需要 1000 行机器语言才能完成的事情。换句话说,高级语言的主要目的是使源代码更小。

如果更小的源代码是高级语言的目的,而某种事物的力量在于它实现其目的的程度,那么编程语言的力量衡量标准就是它使程序变得有多小。

相反,如果一种语言不能使你的程序变得更小,那么它就没有很好地完成编程语言应有的功能,就像一把切得不好的刀,或者难以辨认的打印一样。

指标

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

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

我认为,衡量程序大小的更好方法是元素的数量,其中元素是任何如果你画一棵表示源代码的树时会成为不同节点的东西。变量或函数的名称是一个元素;整数或浮点数是一个元素;一段文字文本是一个元素;模式或格式指令的一个元素是一个元素;新块是一个元素。存在边界情况(-5 是两个元素还是一个元素?)但我认为它们中的大多数对于每种语言都是相同的,因此它们对比较的影响不大。

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

设计

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

如果程序的概念负荷与其复杂性成正比,并且给定的程序员可以容忍固定的概念负荷,那么这等于问,我能做些什么才能使程序员完成最多的工作?在我看来,这等同于问,我如何才能设计出一种好的语言?

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

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

比较

据我所知,第一个就这些问题进行论述的人是 Fred Brooks,他撰写了*《人月神话》* 。他写道,无论使用哪种语言,程序员每天编写的代码量似乎都差不多。当我二十出头第一次读到这篇文章时,我感到非常惊讶,而且似乎意义重大。这意味着 (a) 加快软件编写速度的唯一方法是使用更简洁的语言,并且 (b) 不辞辛劳这样做的人可能会让没有这样做的竞争对手望尘莫及。

如果布鲁克斯的假设是正确的,那么它似乎就是黑客行为的核心。此后的几年里,我一直密切关注我能找到的有关这个问题的任何证据,从正式研究到个别项目的轶事。我没有发现任何与他相矛盾的东西。

我还没有看到我认为具有决定性的证据,我也不指望能看到。像 Lutz Prechelt 的编程语言比较研究,虽然产生了我所期望的结果,但往往使用太短而无法成为有意义的测试的问题。对一种语言更好的测试是花一个月编写的程序中发生的事情。如果你和我一样相信一种语言的主要目的是便于思考(而不仅仅是在你想到之后告诉计算机该做什么),那么唯一真正的测试就是你能用它写出什么新东西。因此,任何必须满足预定义规范的语言比较都是在测试错误的东西。

对一种语言的真正考验在于你能多好地发现和解决新问题,而不是你能多好地用它解决别人已经提出的问题。这两个标准截然不同。在艺术领域,如果你事先知道自己想做什么,那么刺绣和马赛克等媒介效果很好,但如果你不知道,它们就绝对糟糕。当你想在制作图像时发现图像时——例如,你必须处理任何像人物图像这样复杂的东西——你需要使用更流畅的媒介,如铅笔、水墨画或油画。事实上,挂毯和马赛克的制作方式实际上是先画一幅画,然后复制它。(“卡通”这个词最初是用来描述为此目的而画的画的)。

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

尽管来自该领域的报告必然不如“科学”研究那么精确,但可能更有意义。例如,爱立信的 Ulf Wiger 进行了一项研究,得出的结论是 Erlang 比 C++ 简洁 4 到 10 倍,并且在以下方面开发软件的速度也更快:

爱立信内部开发项目之间的比较表明,包括软件开发的所有阶段,生产线/小时生产率相似,而与使用哪种语言(Erlang、PLEX、C、C++ 或 Java)无关。不同语言之间的区别在于源代码量。

这项研究还明确指出了 Brooks 书中只含蓄地提到过的一个观点(因为他测量的是调试代码行数):用功能更强大的语言编写的程序往往缺陷更少。在网络交换机等应用中,这本身就成为目的,可能比程序员的生产力更重要。

味觉测试

最终,我认为你必须听从自己的直觉。用这种语言编程感觉如何?我认为找到(或设计)最佳语言的方法是变得对一种语言让你思考的程度高度敏感,然后选择/设计感觉最好的语言。如果某些语言功能不方便或有限制,不要担心,你会知道的。

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

我不是唯一一个。我知道很多 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)))))

虽然我一下子想不出任何例子,但我对语言是否过于简洁这个问题很感兴趣。是否有语言迫使你以一种晦涩难懂的方式编写代码?如果有人有例子,我会非常感兴趣。

(提醒:我正在寻找的是按照上面概述的“元素”度量非常密集的程序,而不仅仅是因为可以省略分隔符并且所有内容都有一个字符名称而较短的程序。)