编辑程序“,而他们正在运行”? 为什么?

最近我已经更多地使用了Lisp和Lispy语言,而且我发现它们非常强大。

我一直在网上阅读的一件事是,在Lisp,Clojure等中编写的好处是你可以在程序运行时编辑你的程序。

也许我错过了什么,但是有什么意义呢?

当然,这可能会节省几秒钟,但所有? 每当我改变我的程序,我就停止它,然后再次启动,这已经好几十年了。

除了节省时间之外,还有一个原因 – 它是什么?

有人能给我一个很好的案例研究,会让我stream口水这个function吗? 🙂

期待stream口水!

有一些非常酷的用例。 一个例子是在GUI编程中 – 我在实际开发GUI应用程序时看到了这一点,因为它运行在我的Emacs旁边:我添加了一个新button的代码,点击“Cc Cc”编译该单一function,在窗口! 没有closures并重新打开应用程序。 然后,我开始调整小部件并操纵布局,打开的窗口会立即重新排列 – button会四处移动,新的文本字段会popup来,等我执行了每一个小小的改变。

另一个例子是关于Clojure OpenGL库“Penumbra”的优秀截屏,程序员可以实时创build3D tetris游戏。 他从他的emacs旁边的一个空的OpenGL窗口开始。 他定义了一个立方体对象 – CMx,它在屏幕上。 运行命令旋转它,立即开始旋转。 运行一个循环,在不同的位置定义5个多维数据集,出现popup式stream行音乐popup式对话框。 这一切都立即响应,完整的OpenGL工具包就在那里玩。 添加一个新的表面纹理到你的立方体,看到它立即出现。 它变成了一个可延展的3d世界 – 代码dynamic地修改了现有的世界,而不是closures和重新打开每一个变化的3Dcanvas。

Penumbra Livecoding Screencast – 下载高清版本以获得最佳体验。

还有一个关于Clojureaudio库“Overtone”的精彩演示/截屏。 该库是一个合成器工具包,您可以使用一组合成器函数来操作声波。 在演示过程中,开发人员编写一段代码,开始进行音调播放。 然后他用十秒的时间写一个循环播放这个声音10次,但是每次都使频率更高,CMx又听到了,音符越来越高。 在20分钟的时间里,他得到了一首歌。 它看起来很有趣。

Overtone演示文稿链接

其他用途将是,例如:Web爬行/数据挖掘 – 开发和改进实时提取信息的algorithm,查看每个步骤返回的数据; 机器人编程 – 在机器人正在运行时向机器人发送命令; 面部/图像识别 – 像OpenCV这样的库可以监视您的更改,并在开发代码时立即更新图库在video中识别的内容; math工作(Clojure有统计“Incanter”); 以及您希望立即看到您的更改对您正在使用的数据有什么影响的任何环境。

所以这是在您面前展示REPL最有趣的方面。 事情不是有形的,可塑的,互动的,开始成为现实。 GUIdevise,3Dgraphics,程序化声音制作,提取和转换数据,这些事情通常都是公平的。 但是在Clojure中(在某种程度上也和其他dynamic语言一样),它是非常直接的, 当你编写代码的时候,你会看到每一个变化,如果某个东西不起作用,或者你没有得到你期望的结果,你只需要改变你错过的东西并立即重新执行。

Clojure非常赞同这样做。 疯狂的事情是,你可以用同样的方式实时使用Java库 – 尽pipeJava本身不能这样做! 所以Overtone实时使用Java合成库,尽pipe你永远无法使用Java,Penumbra使用Java OpenGL绑定等等。这是因为Rich Hickeydevise了Clojure,因此它可以在运行中编译为JVM字节码。 这是一个了不起的语言 – Clojure已经做了巨大的贡献,如何令人难以置信的有趣和富有成效的编程。

除了节省时间之外,还有一个原因 – 它是什么?

不,没有。 我的意思是, 从来没有 :使用电脑的全部理由是为了节省时间。 没有什么电脑可以做,你不能手工做。 这只需要一点时间。

在这种情况下,我不会放弃“几秒钟”,因为这是我整个编程生涯中一天比以往任何时候都要做的更多的事情之一。 几秒钟重新编译,几秒钟重新运行,几秒钟重新创build我的程序以前的状态 – 即使是在一个快速的工作站上,在迭代之间也可以轻松一分钟。 (之前的版本更糟糕,但是更快的硬件只会让它变得不那么糟糕,不好,整个文件或者更糟糕的重新编译都是I / O绑定的,可能永远不能匹配更细粒度编译的速度。

在Lisp中,在一个已经运行的过程中重新编译单个函数几乎是瞬间的(即使在我5岁的笔记本电脑上,我也从未见过它,甚至在0.1秒内),并重新启动意味着我不必重新创build我的状态,即使有什么信号。

这里有一个工具,可以让我把我作为程序员做的最慢,最常见的事情之一加快了100倍。 我不知道你还需要什么 我们大概可以想出一些理由,但如果这不够理智,我不知道会是什么。 嗯,这也很酷吗? 🙂

(*只要有人说“从不”涉及技术问题,那么2年后这个人总是会变成一个完全白痴的人,尽pipeLisp长寿了,但我肯定也不例外)。

Lisp有一个营销口号:

使用Lisp及其增量开发方法,改变软件系统的成本取决于改变的大小,而不是整个软件的大小。

即使我们有一个大型的软件系统,一个变更的成本(时间,…)仍然与变化的大小有关。 如果我们添加一个新的方法或改变一个新的方法,努力仍然与编辑方法相关,增量编译方法和递增加载方法。

在许多传统的软件环境中,一个方法的改变可能需要一个部分重新编译,一个新的链接可执行文件,一个重新启动,重新加载等等。软件越大,需要的时间越长。

对于一个人来说,这意味着,我们可能脱离了一种stream动状态 。 这是良好的Lisp环境的生产力的一部分:一旦程序员感觉舒适并进入这种stream动状态 ,就可以在很短的时间内对软件系统进行很多改变。 我想很多人都经历过这样的情况:工作在短时间内完成,而不是在一个没有反应的系统前面,而我们面对等待时间的时候。

而且我们和我们正在进行的项目之间的认知距离也很小。 例如,如果您在批处理环境中编辑类,则必须想象这些更改会产生的效果。 在Lisp中,您可以编辑一个类并同时更改对象本身。 这意味着在批量编辑 – 编译 – 链接 – 运行 – testing循环之后,您直接更改对象的行为 – 而不是新版本的对象。

在Lisp系统中,您可以在CAD系统中更改类,然后立即激活它。 当人们问,如果Lisp为大型软件团队工作,答案可能是大型软件团队没有必要,如果你增量工作。 那么问题是,熟悉增量开发的熟练的软件开发人员很less。

在许多应用程序中,有一个独立的脚本语言层,有时候是为原始开发人员(而不是用户)。 在Lisp中这不是必须的, Lisp是它自己的扩展语言

在现实世界中,这主要用于开发,并且像许多function一样,只有在正确的环境下才值得stream口水。

  1. 个人程序员启蒙幸福*
  2. 真正的持续部署。
  3. 零计划停机时间服务水平协议。
  4. debugging生产服务器。

*不是保证。


对我来说,我怀疑其他人在这里的REPL驱动的发展的真正好处是,它可以是无法形容的乐趣。 甚至上瘾。 有时候真的会给人一种手工编码的感觉。 给它一个尝试…来人试试看,首先REPL的总是免费:)


这些天大抽奖是不断的部署。

目前持续部署的想法是,你改变一件事,build立一切(或包装),然后部署。 使用lisp模型,实际上可以在部署时编辑已部署的(通常是接收真实客户会话镜像的框)框。

只是一个迂腐的笔记。 你实际上不会编辑正在运行的类。 您编译该类的新副本,并将其保留在已知位置(var),然后在下一次使用时find并使用新副本。 它在运行时并不真正编辑,而更像新代码立即生效,这将开发过程的范围从程序减less到expression式(通常是函数)。


另外一个stream口水的地方是获得安全修复的好处, 而不必声明任何停机时间 。 你可以进行升级而不必花费你的SLA任何宝贵的“计划停机时间”。 如果你必须提前六个月安排计划停机时间,那么你只能得到两个小时的时间(对于这些可怜的人),真的可以让他们stream口水。


如果您在运行中的应用程序中部署了repl访问权限(可能(有权限)在客户站点上),则可以在运行时连接到应用程序,并在现有上下文中对现有代码运行testing,而不必停止并连接一个debugging器。 你也不会从debugging器中获得任何速度损失。 用REPL可以做到这一点,但是当你得到repl的时候,你可以很容易地创build新的代码(有些人会说通过debugging器注入dynamic类加载器很容易),然后修复。 所以你可以连接到正在运行的服务器。 发现一个函数在短暂的中断后无法重新连接到数据库,然后在那里和那里重新连接。


就像所有的编程结构一样, 永远不会有银弹,而这种持续的部署/开发有一个有趣的缺点:你的程序在内存中是正确的,在磁盘上是错误的。 如果你编译一个函数,然后将其分解并保存,那么代码的唯一工作副本就是正在运行的一个。 在保存这些文件和重新评估文件之后,我很有必要意识到这一点。


这可能听起来很奇怪,所以去检查如何将Clojure REPLembedded到生产应用程序中

我记得有人从美国宇航局描述他的经验。 他的团队在上个世纪70年代实施了太空飞船的软件。 当发现一些错误时,他们有效地修改了软件。

或者想象一下你有一个漫长的过程需要花费数天的时间来执行,最后由于权限或其他小问题而无法写入结果。

还有一个例子。 你正处于整合阶段,你必须做很多小的改变。 还有很多。 我梦想Java的这种可能性,因为目前需要30-40分钟来重build并重新安装我的应用程序(10分钟内重新构build)。

如果你看一下像Erlang这样的东西,关键是避免停机。

它运行在像电话交换机这样的东西,你不能closures几秒钟。

对于更普通的用途,虽然这是一个“很好有”的function,但是,可能并不重要。

你看到真实的数据。 这是一个很大的优势。 那么你就不必猜测了。

因为你能?

严重的是,只要试一试,你会感到痛苦,当你回到你的旧编程语言没有REPL。

即时反馈,轻松进行快速testing,无需在testing夹具中设置假程序状态,能够检查正在运行的程序的状态(该variables的值是多less)。 所有这些都是实时的储户。

这主要是为了发展,这只是一个节省时间。

但是节约时间是非常重要的。

一旦你习惯了回到旧的方式感觉就像从飞溅到焦油游泳一样。

在工业系统中,这用于PLC编程,以减less停机时间和不安全的条件。

这些系统是用于核电站,制造系统,钢铁厂等。这个过程总是在不断地运行,停机时间非常昂贵或不安全。 设想一个控制核反应堆冷却的系统,你不能closures系统来部署新的代码,你必须能够在运行时修改它。

这与电话交换系统的Erlang答案类似。

那么,想象你需要修补一个服务器,而不是阻止它。

如果你用“典型”的语言来做这个,那将会涉及到一些沉重的魔法。 你必须在执行代码的后面钻研。 我认为它需要修补函数表等等,所有这些都在汇编和操作指针function。 一个错误的好地方。

在Lisp中,无停机更新的思想被embedded到语言模型中。 虽然有一些更新的复杂性,你不能摆脱(你如何处理一个长期的连接),它不需要编译语言的沉重魔力。

虽然我没有花费大量的时间(即任何有用的东西),但我确实在Common Lisp中创build了一个服务器的原型,至less可以在networking上进行一些实时修补而不会停机。

另一件好事,除了不用重启所有东西而修改程序(已经做了几十年,并不意味着它是最好的东西,对吗?),是你可以检查你的程序在当前的状态和存在能够弄清楚发生了什么事情。

Casey Muratori刚刚在C和微软的C / C ++编译器上做了一些教训。 其实很简单,只有几十行代码。 看看video22/24/25:

https://www.youtube.com/watch?v=WMSBRk5WG58

在游戏devise中,理由是能够更快速地调整常量,以find您所期望的情感状态。 诸如游戏感觉,非玩家行为脚本和套装灯光/氛围之类的东西从中受益匪浅。