内部可重编程性
2013年1月10日
我正在编程,想在当前输入位置上方添加一个空行。我使用的编辑器没有内置此功能,而且我终于有了这个愿望,我真的想要它。我快速搜索了一下谷歌,找到几行代码,将它们粘贴到我的启动文件中,执行它们,然后我就可以用一个按键在上面创建空行了。只花了不到两分钟,我不用安装任何插件,也不用重启编辑器——这对 Emacs 用户来说是日常工作。
Emacs 是一款老牌软件,可以追溯到 70 年代中期。它允许人们通过修改实时环境来轻松扩展它的理念,与其他一些老牌但开创性的系统共享,例如 Lisp 机器和 Smalltalk。
这种理念现在似乎更少了。当然,有很多可扩展的系统,你可以为 Firefox 等浏览器和 Eclipse 等编辑套件安装插件。整个自由/开源运动都是关于让你访问运行你机器的代码,这样你就可以(理论上)根据自己的意愿调整它。但这些环境中的扩展与你在 Emacs 或 Smalltalk 中进行的重编程之间存在着明显的差异。关于如何轻松快速地进行小的修改,例如我上面添加的新命令。它也是关于在不离开环境的情况下进行操作——我不启动一些单独的工具链来添加 Emacs 函数,而是在 Emacs 本身中工作。
这也与添加某种“宏功能”的工具不同。添加一个新的 elisp 函数正是 Emacs 本身编程的方式——你编程的小扩展与软件的核心编程之间没有区别。这种统一性使你能够深入编辑器的内部。这也意味着你的修改不会被降级到某个“脚本”菜单——它们与工具的任何其他部分都无法区分。
这种能力也是关于你如何与工具相关联的一种理念。对于许多人来说,你使用的软件是一个相对固定的产品。即使是插件也添加了相对有限的选项菜单。内部可重编程工具允许你添加或更改软件的任何部分,使你能够根据自己的比喻手来定制工具。
这种思考甚至适用于编程语言本身。Lisp 和 Smalltalk 都是最小化的语言,它们使你能够轻松地扩展语言,以使扩展看起来与核心相同。这两种语言都没有任何特殊的语法来表示诸如条件逻辑之类的基本语言特性。这种灵活性使 Smalltalk 能够在没有任何语言更改的情况下添加异常处理。
内部可重编程性最大的问题之一是软件实例的碎片化。当我用很多个人函数修改我的 Emacs 实例时,我正在创建我自己的 Emacs 自定义版本,该版本与我机器上的 Emacs 配置紧密耦合。不可避免地,这会引发关于处理核心应用程序升级以及如何轻松地与他人共享我的函数的问题。
具有插件架构和宏语言的系统通过减少定制的表面积来处理这个问题,但正如Nic Ferrier所说:“可重编程系统非常强大。滥用权力总是可能的,在可重编程系统中,人们必须能够滥用它,这是一个原则问题。”
当然,Emacs 社区就是一个很好的例子,说明了这一点在实践中的进展。Emacs 已经足够稳定,以至于尽管定期更新,但大多数人都能够在没有严重头痛的情况下进行升级。Emacs 使用了包管理系统来帮助分发可共享的更改——自最初的 Emacs 和 Smalltalk 时代以来,人们在如何共享代码方面取得了很大进展。分布式版本控制工具的兴起为管理共享代码提供了更多想法。
对于大多数开发人员来说,也许他们最接近内部可重编程性理念的是 Unix 命令行 shell。我认为这是一个(简单的)例子,因为任何人都可以轻松地创建一个新命令,并将其添加到系统中,使其与内置命令无法区分。包允许人们添加更多命令,从而根据自己的特定需求调整他们的命令行环境。命令行的交互模型不像 Emacs 和 Smalltalk 那样丰富,所以它只是这些环境提供的功能的微弱反映,但它确实暗示了这种体验。
如果内部可重编程性对于面向程序员的工具来说很少见,那么对于面向非程序员的工具来说就更少了。我经常想知道这是否应该改变。如果让更多工具表现出这种品质会带来什么?这是否会鼓励更多人学习编程,以便更好地控制他们花费大量时间在其中的环境?这当然也是Alan Kay 对 dynabook 的愿景的一部分。他认为孩子们不是媒体的被动消费者,而是积极地编程他们的环境。
编程并不容易,我不会低估程序员每天面临的挑战。但这并不意味着内部可重编程性应该被降级到 20 世纪 70 年代对未来的愿景。现代 dynabook 缺乏 Kay 愿景中的内部可重编程性的一个重要原因是,它还没有被列为优先事项。也许这是我们应该更多地思考的事情。
修订
2020-02-24:更新以提及 Unix 命令行并删除一些过时的材料