我终于坐下来写有关软件方面的文章
对我来说最重要的工程。

它是编译时计算。 编译时计算意味着您以后编译的代码不仅仅是您写下的代码。 以后编译的也是由代码“编写”的代码。 代码生成代码。 宏,但不是杂乱的标记宏。 在Lisp中,您甚至可以使用相同的语言,从而可以在编译时和运行时将所有以前的工作重新用于计算。
现在有什么意义? 如果做得正确,编译时计算使您可以将在编程过程中所做的每个假设都放在一个地方。
每次将一个假设泄漏到一个以上的位置中时,当您以后对该假设改变主意时都会产生问题。 我想这里的每个人都知道,在编写程序之前,对程序进行合理,完整的规范从未发生过,也永远不会发生。 更改是软件开发的本质,在程序首次发布之前,更改已经开始了很长时间。 您需要能够改变关于假设的主意,并为您的每个旧假设使用一个中央控制旋钮。 您要更改一个位置,并使其在整个系统中传播,因为您要在那个位置更改一个假设。 每个假设只有一个人为书写,由人维护的地方。
首先打印小图:@ dcooper8编辑帮助。 非常感谢 这是在我仍在QPX上为Google工作时写的,现在不再如此了。 本文是我撰写有关程序员的注意力和注意力管理的重要部分,另请参见 https://medium.com/@MartinCracauer/on-attention-focus-and-autism-in-the-tech-workplace-8246526fbbc0
首先让我们看看其他语言在做什么:
几年前,C ++社区中的编译时计算有点流行,当时模板变得足够强大,可以执行少量的编译时计算(此后我将其缩写为“ CTC”)。 由于C ++中的编译时编程语言存在严重局限性,因此它的进展不大(该语言仅具有一种数据类型(C ++类型)和仅一种控件构造(递归),并且没有集合类型的概念)并反复遍历它们)。 我最终感到失望。 我仍然很高兴它发生了,因为它使CTC在很多人的脑海中浮出水面。
CTC的其他形式包括C和C ++预处理器中的宏,各种形式的m4 / m5使用和D语言,所有这些在表达能力和与主要语言进行交互的能力上均受到限制。 在我的博客系列中,我将使用Common Lisp(“ CL”),因为它是目前唯一已完全开发了编译时间计算的语言。 我知道,这使得要吸引大量的听众很难。 即使在我看来,CL仍然存在问题点,但这正是编译时计算所需要的。 无论CL的缺点是什么,使用CTC已有数十年之久,我都会没有它。 没有什么可以让我们编写在30多年后仍会自愿维护的代码了。 其他所有内容都是一次写入操作,然后要么是“维护旧版系统的痛苦”,要么是源源不断的带有相关第二系统效果的重写流。
在Common Lisp中,编译时计算使用相同的语言进行编译和运行。 您在CL中编写的每个函数都可以在运行时(例如在“常规”程序中)或在编译时(根据集中的规范生成或修改其他代码)调用。
要遵循代码吗?
现在,对于想要遵循示例的读者来说,这需要少量的设置工作。 您必须具有Common Lisp(CL)环境才能尝试此操作。 相当少的事情可以做到:
1.您必须具有一个代码编辑器,该代码编辑器显示匹配的括号(当键入一个右括号时,它将突出显示匹配的开放括号)。 你必须有这个。 Lisp无法使用其他所有功能。 如果可以的话,它也应该具有自动缩进功能。
2.您应该具有一个Repl(命令行),该代码允许您重复和编辑命令。 您可以使用将GNU readline用于其输入的CL实现(例如CLISP),也可以在Emacs shell缓冲区中使用诸如SBCL之类的实现。
3.您可以设置SLIME,这是Emacs内部的一个真正的IDE。 我的示例非常简单,您将不需要它。 另一方面,Slime可能有助于调试器的使用并探索语言。
4.如果要使用任何第三方库(我的示例不是必需的,但适合个人实验),请查看Quicklisp
我建议在Emacs中使用SBCL。 SBCL是带有编译器的高性能实现,可以使您通常编写无开销的代码。 这意味着编译后的代码可以像C代码一样快地运行(有例外和hack,请参见我的会议上有关ITA如何使用SBCL的讨论)。 它还具有一套不错的编译时警告,包括类型不匹配警告,以及一个蓬勃发展的社区,它积极开发并为人们提供帮助。 SBCL适用于Linux,BSD和各种处理器,包括在Raspberry Pi,OSX甚至Windows上运行的ARM。 [编辑的注解–您可以从gendl.org获得适用于Linux,Mac和Windows的免费的预编译安装包,其中包含Emacs,Slime和Gendl(基于CCL,但也计划提供SBCL)。
替代方案:已做出各种努力来建立与其他语言中的Lisp宏相关的机制—通常是Lisp派生,这些派生试图通过删除基于括号的语法来使Lisp看起来更易于访问。 在我将在此处概述的意义上,我还没有发现它们允许完整的编译时计算。 它始终是“我们实现了CL宏的最常用用法”,我发现它们通常会删除我需要做的事情。 我将非常感谢读者们为我在Dylan或任何基于JVM的Lisp派生工具中所展示的实现做出贡献。 现在,我将坚持使用CL。 不要误会我的意思。 即使在我看来,一旦您足够聪明的宏,CL就会很难阅读。 但是,如果考虑到这些宏提供了什么样的表达能力,以及与在整个源代码中散布事实相比,它们使事物更可维护的话,这看起来是合理的。 我将全力以赴拥有功能完善的编译时计算的更好语言。
Lisp宏还可以显着提高可读性 。 一个早期的例子是Lisp编译时计算使您可以创建所需的任何文字对象-无需编译器或解析器支持。 如果要在源代码中将文字对象烘焙到二进制文件中,则该二进制对象是一个哈希表,指向一堆具有复数的数组-没问题。 即使该语言还没有所需文字的语法,也可以借助编译时计算来添加它。 但是,您将不得不克服括号的问题,为此,您需要一个带有括号匹配显示的编辑器,并且理想情况下还需要Lisp的缩进支持。
最后但同样重要的是,还有Diff效率。 什么是“差异效率”? 这意味着,如果您查看代码的git diff与代码的不同状态,那么您只会看到实际的功能更改。 没有很多样板。 复制并粘贴了该功能更改的少量变体。 如果您使用语法突出显示,则它将正确运行,因为您没有在主语言的源代码中嵌入外来语言。 一个假设,一个地方。 一个假设改变了,一个地方改变了。
(旁注:“帮助”您做大样板的IDE会自动更改,但是当您希望看到差异值得一掷千金时,不要重新折叠它们)
综上所述:
编译时计算使您可以将编程过程中所做的每个假设都放在一个地方,从而大大提高了可更改性。
程序员实际上是想这样做的-但是不可减少的重复使他们感到沮丧。 我们的工具糟透了。
Common Lisp是我目前用于完整编译时计算的语言。
在主要的源代码中,您将很少使用“自定义”微型语言来将信息集中在紧凑的空间中。 里面的主要源代码。 无需在特殊标记内使用其他语法。 您将不需要使用带有解析器的外部程序,而不必通过Python生成代码。 这些迷你语言只是定义了在源代码中表达您想要的内容的方法,这些方法最方便您告诉它们。 然后,宏使它们可用,而无需修改编译器或调用任何外部工具。 副作用是,它极大地降低了程序所需的样板数量。 Boilerplate不利于以后的更改,因为它会炸裂更改所产生的差异的大小,并且您看不到给定差异的“要点”,因为所有接口调整都对这一更改没有影响,因为接口调整已全部完成。
关于我自己:汉堡大学AI系的Rainer Joswig @ RainerJoswig向我介绍了Common Lisp(非常感谢!)。 1992年(我学习了物理,但谁在数?)。 当时我在Smalltalk中寻找所有这些问题的解决方案。 这是一流的介绍,使用运行Genera的Symbolics Lisp机器(稍后会介绍),以及当时著名的Macintosh Common Lisp。 我开始在工作中使用CL和FreeBSD,选择了CMUCL,最终完成了开源CMU的Common Lisp的工作-再次感谢Scott Fahlman @ ScottFahlman。 CMUCL后来发展为SBCL。 在2000–2001年,我被ITA软件聘用,以使其QPX搜索引擎在开源CMUCL上稳定运行,为第一批商业QPX客户服务。 (我也衷心感谢您在Orbitz的每个人。与您合作很荣幸)
事实是,Lisp带给我们的可维护性和改变您的想法的能力是ITA成功的关键因素。 如果您在航空公司定价领域工作,那么您真的需要有能力根据所有这些有趣的票价规则(至少包括图灵完整的规则) 真正意味着什么来改变您的假设。 ITA的成功导致被Google收购,而我仍然在今天(更新-不)。 15年后仍在与同一个代码库作战,尚不清楚谁在获胜(不,真的,我爱QPX)。
第二部分:https://medium.com/@MartinCracauer/a-gentle-introduction-to-compile-time-computing-part-2-cb0a46f6cfe8