Featured image of post 简洁就是力量

简洁就是力量

探讨编程语言的简洁性与其表达能力的关系,以及如何通过简洁性提升编程效率。

📚 返回 Paul Graham 文章目录

简洁就是力量

2002年5月

“代数符号将大量含义压缩到小空间中的能力,是促进我们习惯于通过它们进行推理的另一个因素。”

  • Charles Babbage,引自Iverson的图灵奖演讲

在LL1邮件列表上关于Revenge of the Nerds提出的问题的讨论中,Paul Prescod写了一些让我印象深刻的话。Python的目标是规律性和可读性,而不是简洁性。乍看之下,这似乎是对编程语言的一个相当严厉的批评。据我所知,简洁性=力量。如果是这样,那么替换一下,我们得到Python的目标是规律性和可读性,而不是力量。这似乎不是一个(如果是一个的话)你想要的权衡。这几乎等于说Python的目标不是成为一个有效的编程语言。

简洁性=力量吗?这对我来说似乎是一个重要的问题,也许是语言设计者最需要面对的问题,直接面对这个问题会很有用。我还不能确定答案是否就是简单的"是",但这似乎是一个很好的假设。

假设

我的假设是简洁性就是力量,或者足够接近,除了在病态的例子中,你可以把它们视为等同的。

在我看来,简洁性就是编程语言的目的。计算机直接用机器语言告诉它们做什么也会同样高兴。我认为我们开发高级语言的主要原因是获得杠杆作用,这样我们可以用10行高级语言说(更重要的是,思考)需要1000行机器语言才能表达的内容。换句话说,高级语言的主要目的是使源代码更小。

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

相反,一个不能使你的程序变小的语言在编程语言应该做的事情上做得很差,就像一把切不好的刀,或者印刷不清楚的文字。(这些都有其利基市场,比如为儿童制作的塑料刀,或者香烟包装上的警告信息印刷方式,但我认为很少有语言想要处于这样的利基市场。)

衡量标准

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

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

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

这个衡量标准需要完善,对于特定语言可能需要解释,但我认为它在尝试衡量正确的东西,即程序拥有的部分数量。我认为你在这个练习中绘制的树就是你在头脑中必须构建的东西,以便构思程序,所以它的大小与你编写或阅读它必须做的工作量成正比。

设计

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

如果程序的概念负载与其复杂性成正比,而给定的程序员可以容忍固定的概念负载,那么这与问"我能做什么来让程序员完成最多的工作?“是一样的。在我看来,这与问"如何设计一个好的语言?“是等同的。

(顺便说一下,没有什么比设计语言更能明显证明那句老话"所有语言都是等价的"是错误的事了。当你设计一种新语言时,你不断比较两种语言——如果我做x,语言会是什么样,如果我不做,语言会是什么样——来决定哪个更好。如果这真的是一个毫无意义的问题,你还不如掷硬币。)

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

比较

据我所知,第一个写这些问题的的人是Fred Brooks,在《人月神话》中。他写道,程序员似乎每天生成大约相同数量的代码,不管使用什么语言。当我二十多岁第一次读到这个时,这对我来说是一个很大的惊喜,似乎有巨大的含义。这意味着(a)让软件写得更快的唯一方法是使用更简洁的语言,以及(b)如果有人费心这样做,他们可以远远甩开那些没有这样做的人。

Brooks的假设,如果是真的,似乎处于黑客工作的核心。在随后的几年里,我密切关注了任何我能得到的关于这个问题的证据,从正式研究到个别项目的轶事。我没有看到任何反驳他的东西。

我还没有看到在我看来是决定性的证据,我也不期望看到。像Lutz Prechelt的编程语言比较这样的研究,虽然产生了我预期的结果,但倾向于使用太短的问题,无法成为有意义的测试。对语言更好的测试是在需要一个月来编写的程序中会发生什么。如果你像我一样相信语言的主要目的是成为思考的好工具(而不是仅仅告诉计算机一旦你想到了要做什么),那么唯一的真实测试是你能用它写出什么新东西。所以任何你必须满足预定义规范的语言比较都在测试稍微错误的东西。

语言的真实测试是你发现和解决新问题的能力,而不是你用它解决别人已经制定的问题的能力。这两个是完全不同的标准。在艺术中,像刺绣和马赛克这样的媒介,如果你事先知道要做什么,效果很好,但如果你不知道,就完全不行。当你想在制作过程中发现图像时——就像你必须对任何像人物图像这样复杂的东西所做的那样——你需要使用更流畅的媒介,如铅笔、水墨或油画。事实上,挂毯和马赛克的制作方式是先画一幅画,然后复制它。(“卡通"这个词最初是用来描述为此目的而画的画)。

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

来自实地的报告,虽然必然不如"科学"研究精确,但可能更有意义。例如,Ericsson的Ulf Wiger做了一项研究,得出结论说Erlang比C++简洁4-10倍,相应地软件开发速度也更快:Ericsson内部开发项目之间的比较表明,无论使用哪种语言(Erlang、PLEX、C、C++或Java),包括软件开发的所有阶段在内的每小时生产率都相似。那么不同语言的区别就变成了源代码量。这项研究还明确处理了Brooks书中只是隐含的一个问题(因为他衡量的是调试后的代码行数):用更强大的语言编写的程序往往有更少的bug。这本身就成了一个目标,在网络交换机这样的应用中可能比程序员生产率更重要。

品味测试

最终,我认为你必须跟着感觉走。用这种语言编程感觉如何?我认为找到(或设计)最好语言的方法是变得对语言让你思考的能力高度敏感,然后选择/设计感觉最好的语言。如果某个语言特性笨拙或限制性,别担心,你会知道的。

这种高度敏感会带来代价。你会发现你不能忍受用笨拙的语言编程。我发现用没有宏的语言编程令人难以忍受地限制,就像习惯了动态类型的人发现不得不回到必须声明每个变量类型,不能制作不同类型对象列表的语言编程令人难以忍受地限制一样。(这就像那些你必须回到高中的噩梦之一。)

我不是唯一一个。我认识很多Lisp黑客都经历过这种情况。事实上,编程语言相对力量最准确的衡量标准可能是知道该语言的人中,不管应用领域如何,愿意接受任何能使用该语言的工作的人的百分比。

限制性

我认为大多数黑客都知道语言感觉限制性是什么意思。当你感觉那样时发生了什么?我认为这与当你想要的街道被堵住,你必须绕远路才能到达你想去的地方时的感觉是一样的。有一些你想说的话,但语言不让你说。

我认为这里真正发生的是,限制性语言就是不够简洁的语言。问题不仅仅是你不能说计划要说的话。而是语言让你绕的路更长。试试这个思维实验。假设有一些你想写的程序,语言不让你按计划的方式表达它,而是强迫你用其他方式写程序,而且这种方式更短。至少对我来说,这不会感觉很限制。这就像你想要的街道被堵住,但路口的警察指引你走捷径而不是绕远路。太好了!

我认为(百分之九十?)限制性的感觉来自于被迫使你在语言中写的程序比你头脑中的更长。限制性主要是缺乏简洁性。所以当语言感觉限制性时,这(主要)意味着它不够简洁,当语言不够简洁时,它会感觉限制性。

可读性

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

在这里我们必须小心区分单行代码的可读性和整个程序的可读性。重要的是第二个。我同意一行Basic可能比一行Lisp更可读。但用Basic写的程序会比用Lisp写的相同程序有更多的行(特别是当你进入Greenspunland时)。阅读Basic程序的总努力肯定会更大。

总努力 = 每行努力 × 行数

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

每行可读性对第一次遇到语言的用户来说意味着源代码看起来不会吓人。所以每行可读性可能是一个好的营销决定,即使它是一个糟糕的设计决定。它与非常成功的分期付款技术是同构的:与其用高额的前期价格吓唬他们,你告诉他们低的月付款。但分期付款计划对买家来说是净损失,就像每行可读性对程序员来说可能是净损失一样。买家要支付很多次那些低额付款;程序员要阅读很多那些单独可读的行。

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

到什么程度?

很多人拒绝简洁性=力量的想法。我认为与其简单地争论它们是否相同,不如问:简洁性在多大程度上=力量?因为显然简洁性是高级语言的主要目的之一。如果这不是它们的全部目的,那么它们还有什么其他功能,这些其他功能相对有多重要?

我提出这个问题不仅仅是为了让辩论更文明。我真的想知道答案。什么时候,如果有的话,一个语言会简洁到对自己不利?

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

语言,而不是程序

我们应该清楚我们在谈论语言的简洁性,而不是个别程序的简洁性。个别程序当然可能写得过于密集。

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

我确定每种语言都有这样的权衡(虽然我怀疑随着语言变得更强大,赌注会变得更高)。每个程序员一定都见过一些聪明人通过使用可疑的编程技巧使代码稍微变短的代码。

所以关于这一点没有争论——至少,从我这里没有。个别程序当然可能简洁到对自己不利。问题是,语言能吗?语言能强迫程序员以牺牲整体可读性为代价写更短(在元素方面)的代码吗?

很难想象一个语言会太简洁的一个原因是,如果有某种过于紧凑的方式来表达某事,可能也会有更长的方式。例如,如果你觉得使用很多宏或高阶函数的Lisp程序太密集,你可以,如果你愿意,写与Pascal同构的代码。如果你不想在Arc中将阶乘表达为高阶函数的调用(rec zero 1 * 1-),你也可以写出递归定义:(rfn fact (x) (if (zero x) 1 (* x (fact (1- x)))))

虽然我现在想不出任何例子,但我对语言是否可能太简洁这个问题感兴趣。有没有语言强迫你以难以理解和难以理解的方式写代码?如果有人有例子,我会非常感兴趣看到它们。

(提醒:我在寻找的是根据上面概述的"元素"衡量标准非常密集的程序,而不仅仅是那些因为可以省略分隔符而变短,而且所有东西都只有一个字符名称的程序。)

英文版:paulgraham.com/power.html|中文版:HiJiangChuan.com/paulgraham/016-succinctness-is-power

📚 返回 Paul Graham 文章目录

更新记录: