Local Reasoning for Global Properties

Local Reasoning for Global Properties

局部推理与全局属性

In the last couple of years, I’ve increasingly been asked questions that boil down to: will AI benefit from new kinds of programming languages? My answer has been “probably not” and, so far at least, that answer has held up well: AI is now able to generate large quantities of code in just about any programming language you or I can think of. 在过去几年里,我越来越多地被问到这样一个核心问题:人工智能会从新型编程语言中受益吗?我的回答一直是“可能不会”,而且至少到目前为止,这个答案依然站得住脚:AI 现在已经能够用你我能想到的几乎任何编程语言生成大量的代码。

Now that the technology has advanced, and its characteristics have started to become clearer, my answer has changed. My experience is that AI – at least as it stands right now – often generates high-quality local (e.g. a function) chunks of code, but often struggles when asked to generate code that requires a global understanding of the program. 随着技术的进步及其特性逐渐明朗,我的答案发生了改变。我的经验是,AI(至少就目前而言)通常能生成高质量的局部代码块(例如函数),但在需要对程序进行全局理解才能生成的代码面前,往往显得力不从心。

The easiest way to see this is a proliferation of unnecessary defensive checks: these seem benign, but can cause an exponential increase in the number of states later readers of the code believe can occur, with all the deleterious effects that implies. Perhaps this struggle will soon be overcome, but if it isn’t, we might once again look to programming language design for help. 最直观的表现就是大量不必要的防御性检查:这些检查看起来无害,但会导致后续阅读代码的人认为程序可能出现的状态呈指数级增加,并带来随之而来的各种负面影响。也许这种困境很快会被克服,但如果不能,我们可能需要再次寻求编程语言设计的帮助。

My aim in this post isn’t to try and predict the specific ways that programming languages will, or even should, try to address this. Instead I want to answer a more basic question: do we have a good example of programming language design that allows local reasoning to give us assurance about a surprising global property? 我写这篇文章的目的并不是要预测编程语言将如何(甚至应该如何)解决这个问题。相反,我想回答一个更基本的问题:我们是否有编程语言设计的优秀范例,能够让我们通过局部推理来确保某种令人惊叹的全局属性?

Background

背景

I’ve made a fair chunk of my living out of programming languages, so I have a vested interest in amplifying their importance. However, while I believe that programming languages do have some influence on our productivity, and on the reliability of the software we create, there isn’t much evidence that they make a profound difference. 我的大部分收入都来自编程语言领域,因此我个人非常希望强调它们的重要性。然而,虽然我相信编程语言确实对我们的生产力和所创建软件的可靠性有一定影响,但并没有太多证据表明它们能产生深远的影响。

I don’t just mean “no-one’s been able to do a good experiment which proves there are differences” — though that is true! Rather, a lot of “good” software has been created in “bad” languages and a lot of “bad” software has been created in “good” languages. It seems unlikely that the particular programming language used was the main influence on such outcomes. 我指的不仅仅是“没有人能做一个好的实验来证明它们之间存在差异”——尽管这确实是事实!相反,许多“优秀”的软件是用“糟糕”的语言编写的,而许多“糟糕”的软件则是用“优秀”的语言编写的。看来,所使用的特定编程语言似乎并不是影响这些结果的主要因素。

The simplest argument for this is that creating software that does everything its users need, in a comprehensible and reliable way, requires empathy more than it does expertise in challenging programming language features. For a slightly more nuanced view, I’ve previously tried to capture my thoughts on the nature of software. 对此最简单的论点是:要创建出既能满足用户所有需求,又具备可理解性和可靠性的软件,所需的更多是同理心,而非对复杂编程语言特性的精通。为了提供一个稍微细致一点的视角,我之前曾尝试记录过我对软件本质的思考。

This shouldn’t be taken as me saying that programming languages don’t make any difference. When I moved from programming in assembler to “high” level languages like Python and C, my productivity increased substantially and I felt able to tackle much larger pieces of software. The reason is simple: assembly forces me to deal with so many low-level details that I continually forget the more important high-level picture. The difference in the software I could create was profound. 这不应被理解为我说编程语言毫无影响。当我从汇编语言转向 Python 和 C 这样的“高级”语言时,我的生产力大幅提升,并感到自己能够处理规模大得多的软件。原因很简单:汇编语言迫使我处理太多的底层细节,以至于我不断忘记更重要的宏观全局。我所能创建的软件质量差异是巨大的。

Unfortunately, I gradually came to realise that such a huge improvement was unlikely to be repeated. I had, slowly and ineptly, reinvented Fred Brooks’s no silver bullet argument: Most of the big past gains in software productivity have come from removing artificial barriers that have made the accidental tasks inordinately hard, such as severe hardware constraints, awkward programming languages, lack of machine time. How much of what software engineers now do is still devoted to the accidental, as opposed to the essential? Unless it is more than 9/10 of all effort, shrinking all the accidental activities to zero time will not give an order of magnitude improvement. 遗憾的是,我逐渐意识到这种巨大的进步不太可能再次发生。我缓慢而笨拙地重现了弗雷德·布鲁克斯(Fred Brooks)的“没有银弹”论点:过去软件生产力的大部分重大提升,都来自于消除了那些使偶然性任务变得异常困难的人为障碍,例如严格的硬件限制、笨拙的编程语言、机器时间的匮乏。现在软件工程师所做的工作中,有多少仍然致力于偶然性任务,而不是本质性任务?除非这部分工作占到总工作量的 9/10 以上,否则将所有偶然性活动的时间缩减为零,也不会带来数量级的提升。

An exception

一个例外

That meant that when, in a specific context many, many years later, I experienced another profound change in productivity for a lot of software I write, I was so surprised that I almost didn’t notice. When I eventually did, and tried to explain to other people the difference, they also seemed baffled. The context? Multi-threaded programming in Rust. 这意味着,当许多年后,在特定的环境下,我所编写的许多软件的生产力再次发生了深刻变化时,我感到非常惊讶,以至于差点没注意到。当我最终意识到这一点并试图向其他人解释这种差异时,他们似乎也感到困惑。什么环境?Rust 中的多线程编程。

That experience is what informs my opinion on the best course for programming languages in the future, so I need to convince you that there is something deep in the way that Rust makes multi-threaded programming much easier. Let me start with a concrete example. 那次经历塑造了我对未来编程语言最佳发展方向的看法,所以我需要让你相信,Rust 使多线程编程变得更容易的方式中,蕴含着深刻的道理。让我从一个具体的例子开始。

I wrote the software that builds the website you’re currently reading as normal single-threaded code. Because I’m lazy – and my website isn’t that big – every time I run it, the entire website is rebuilt. After a while, I found that the pauses the software needed to rebuild the site were long enough that they made editing some pages (like this post!) inefficient. I quickly made some single-threaded optimisations, but they weren’t enough. I then guessed that if I could rewrite this to use multi-threading I would get those pauses down to an acceptable level. 我编写了构建你当前正在阅读的这个网站的软件,它原本是普通的单线程代码。因为我比较懒——而且我的网站规模也不大——每次运行它时,整个网站都会被重新构建。过了一段时间,我发现软件重建网站所需的停顿时间太长,导致编辑某些页面(比如这篇文章!)变得效率低下。我很快做了一些单线程优化,但还不够。于是我猜想,如果我能将其重写为多线程,就能将这些停顿时间降低到可接受的水平。

In nearly any other programming language, rewriting the software to use multi-threading would have been a daunting task. Indeed, my past experiences with multi-threading showed me that I would immediately encounter difficult to debug crashes; and, almost certainly, there would be a long tail of such horrors to stumble across over weeks and months. There’s a good reason why I stopped trying to write multi-threaded programs! 在几乎任何其他编程语言中,将软件重写为多线程都是一项艰巨的任务。事实上,我过去的多线程经验告诉我,我将立即遇到难以调试的崩溃;而且,几乎可以肯定,在接下来的几周和几个月里,还会有一连串这样的噩梦等着我去面对。我停止尝试编写多线程程序是有充分理由的!

In this particular case, though, the rewrite – which did indeed solve the performance problems – took me under 5 minutes. It ran correctly on the first try, has stayed working correctly — and I had total confidence that both things would be the case. How can this possibly be? 然而,在这个特定的案例中,这次重写——确实解决了性能问题——只花了我不到 5 分钟。它在第一次尝试时就运行正确,并且一直保持正常工作——而且我完全确信这两点都会实现。这怎么可能呢?