在程序运行时编辑程序?为什么这样做?

70

最近我开始对Lisp和Lispy语言产生更多兴趣,并且发现它们相当强大。

网络上到处都在说,使用Lisp、Clojure等编写代码的好处是可以在“运行时编辑程序”。

也许我缺少了什么,但这有什么意义呢?

当然,这可能会节省几秒钟,但仅此而已吗?每当我对程序进行更改时,我只需停止它,然后重新启动它,这样做已经工作了几十年。

除了节省时间之外,一定还有其他原因,那是什么呢?

能否给我提供一个令人垂涎欲滴的好案例,让我为此功能流口水? :)

期待着畅所欲言!

13个回答

60
有一些非常酷的用例。其中一个例子是在GUI编程中 - 我在实时开发GUI应用程序时看到了这个过程,因为它在我的Emacs旁边运行:我添加了一个新按钮的代码,并按“C-c C-c”编译单个函数,然后该按钮就会出现在窗口中!不需要关闭和重新打开应用程序。然后我开始调整小部件和布局,打开的窗口会立即重新排列自己 - 按钮会移动,新的文本字段会突然出现等等,只要我执行了每个小修改。

另一个例子是关于Clojure OpenGL库"Penumbra"的优秀演示视频,在其中程序员实时创建了一个3D俄罗斯方块游戏。他从一个空的OpenGL窗口开始,旁边是他的emacs。他定义了一个立方体对象-C-M-x-它出现在屏幕上。运行一个命令来旋转它,它立即开始旋转。运行循环定义5个在不同位置的立方体,它们立刻出现。它完全响应,完整的OpenGL工具包就在那里供您使用。添加一个新的表面纹理到您的立方体中,立即看到它出现。它变成了一个可塑的3D世界 - 代码动态修改现有的世界,而不是在每次更改时关闭和重新打开3D画布。

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

这里还有一个关于Clojure音频库“Overtone”的精彩演示/屏幕录像。该库是一个合成器工具包,其中您有一组合成器函数来操纵声波。在演示中,开发人员编写了一些代码,以启动播放音调。然后,他花费十秒钟编写一个循环,播放该声音10次,但每次使频率更高,再次按C-M-x,您会听到音符升高。在20分钟的实时空间内,他开始了一首歌曲。看起来非常有趣。 Overtone演示链接 其他用途可能包括:网络爬虫/数据挖掘-开发和完善提取实时信息的算法,在每个步骤中查看返回的数据;机器人编程-在其实时运行时发送命令;面部/图像识别-使用像OpenCV这样的库,观察您的更改即时更新库在图像/视频中识别的内容,当您开发代码时;数学工作(Clojure具有“Incanter”用于统计);以及希望立即查看所做更改对正在处理的数据产生的影响的任何环境。
那么,拥有一个REPL是最有趣的方面。那些不具体、可塑、互动的事情开始变得具体。GUI设计、3D图形、编程声音制作、提取和转换数据,这些通常都是在远处完成的。但是使用Clojure(以及其他动态语言)可以使其变得非常具体和即时;当您编写代码时,可以立即看到每个更改,并且如果某些内容不起作用或您没有获得预期的结果,则只需更改遗漏的内容并立即重新执行它。
Clojure非常适合做这件事。令人惊奇的是,您可以以实时方式使用Java库-尽管Java本身无法做到!因此,Overtone正在实时使用Java合成库,而Penumbra正在使用Java OpenGL绑定等。这是因为Rich Hickey设计了Clojure,使其能够即时编译为JVM字节码。这是一种令人惊叹的语言-Clojure为编程带来了极大的乐趣和生产力。

2
关于你回答的第一段,你使用了哪个GUI工具包? - Matthias Benkard
Matthias,当时我正在使用Common Lisp中的McCLIM。在Clojure中,我相信人们已经想出了如何使用Swing来完成它。我认为对于你使用的任何GUI工具包,如果你评估你的代码,然后将鼠标移动到窗口上,它就会触发重绘并显示小部件;还有一些技巧可以使重绘发生而不必去寻找鼠标。 - Jeremy Rayman
好的,我已经添加了Penumbra和Overtone演示文稿的链接。我还添加了一个交互式编程帖子的链接,其中包括各种屏幕录像,包括底部与Clojure相关的一些内容。 - Jeremy Rayman
我刚刚看了Overtone的演示:最后15分钟声音出了问题(它播放了演讲开头的音频,所以你听不到他们在说什么)。 - Tatiana Racheva
Clojure的Swing包装器被命名为Seesaw。在那里有相同的体验。 - claj

59
除了节省时间之外,一定有其他原因,是什么呢?
其实没有其他原因。我的意思是,使用计算机的整个原因就是为了节省时间。计算机所能做的事情,你都可以用手完成。只不过需要稍微花费更长的时间。
在这种情况下,“几秒钟”并不可忽视,因为这是我在整个编程生涯中最常做的事情之一,全天候进行。重新编译需要几秒钟,重新运行需要几秒钟,重建上一次程序状态需要数秒钟,即使在快速工作站上,迭代之间往往需要一分钟的时间。(以前要糟得多,但更快的硬件只是让它变得不那么糟糕,而不是变得好。整个文件或更糟的重新编译是受输入/输出限制的,可能永远达不到更细粒度编译的速度。)
在Lisp中,在已经运行的进程中重新编译一个函数几乎是瞬间完成的(即使在我五年前的笔记本电脑上,我从未见过它超过0.1秒),而重新启动意味着即使某些信号发出也不必重新创建我的状态。
这是一个工具,给我提供了比我作为一个程序员最慢和最常见的事情中的任何一个都快100倍以上的速度提升。我不知道你还需要什么。我们可能可以编造一些理由,但如果这还不够的话,我就不知道还有什么其他的理由了。嗯,这也很酷吧? :-) (每当有人谈论技术时用“永远”这个词,那个人最后两年后总是看起来像个笨蛋。尽管Lisp很长寿,但我肯定也会成为例外。)

2
现在是2015年,你说的话仍然是真的。你还不是白痴...但是 ;) - Rekumaru

28

有一个Lisp的营销口号:

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

即使我们拥有一个大型软件系统,对于变更的成本(时间等)始终与变更的规模相对应。如果我们添加一个新的方法或更改一个方法,所需的工作量将与编辑该方法、增量编译该方法和增量加载该方法的工作量成比例。

在许多传统的软件环境中,更改方法可能需要部分重新编译、新的链接可执行文件、重启、重新加载等操作。软件规模越大,所需的时间就越长。

对于人类来说,这意味着我们可能会失去一种心流状态。这是良好的Lisp环境的生产力的一部分:一旦程序员感到舒适并进入这种状态,可以在短时间内对软件系统进行大量更改。我想许多人都经历过这种情况,在短时间内完成了大量的工作-与面对反应迟缓的系统并面临等待时间的时候形成鲜明对比。

此外,我们和正在工作的程序之间几乎没有认知距离。 例如,如果在批处理环境中编辑一个类,则必须想象更改的效果。在Lisp中,您可以同时编辑一个类并更改对象本身。这意味着您直接更改对象的行为-而不是在批处理编辑->编译->链接->运行->测试循环之后生成一个新版本。

在Lisp系统中,您可以更改CAD系统中的类,然后它可以立即生效。 当人们询问Lisp是否适用于大型软件团队时,答案可能是,如果您进行增量式开发,则不需要大型软件团队。问题是/曾经是真正熟悉增量式开发的高水平软件开发人员非常罕见。

在许多应用程序中,存在单独的脚本语言层,有时是为原始开发人员(而不是用户)设置的。在Lisp中,这是不必要的,Lisp是自己的扩展语言


21

在现实世界中,这主要用于开发,并且像许多功能一样,在正确的上下文中才值得吹嘘。

  1. 个人程序员开悟的快感*
  2. 真正的持续部署。
  3. 零计划停机时间服务级别协议。
  4. 调试生产服务器。

*没有保证。


对我来说,以及我怀疑这里的其他一些人,这种REPL驱动的开发的真正好处在于它可以是难以言喻的有趣。甚至令人上瘾。有时它确实能给人一种手工编写代码的感觉。试试看吧...来吧,第一个REPL免费:)


当今的一个重要吸引力是不断部署的想法。

目前连续部署的想法是您更改一项内容,构建所有内容(或打包它),然后部署。使用Lisp模型,实际上可以在部署过程中编辑已部署的盒子(通常是接收实际客户会话镜像的盒子)。

只是一个琐碎的注释。您实际上不会编辑正在运行的类。您会编译类的新副本并将其留在已知位置(变量)中,然后下一次使用它时,将找到并使用新副本。这不是真正的编辑正在运行,而更像是新代码立即生效,这将开发过程的范围从程序扩展到表达式(通常是函数)。


另一个令人垂涎的点是获得安全修复的好处,而无需宣布任何停机时间。您可以进行升级而不会损失SLA中宝贵的“计划停机时间”。如果您必须提前六个月安排计划停机时间,并且只有两个小时(对于这些可怜的灵魂),那么可能会使他们垂涎三尺。

如果您可以访问部署的正在运行的应用程序(可能在客户站点上获得许可),则可以连接到应用程序并在其运行时在现有上下文中运行测试,而无需停止并连接调试器。您也不会因为使用调试器而降低速度。虽然没有REPL也可以做到这一点,但当您将REPL放入其中时,您可以轻松地创建新代码(一些人会说通过调试器注入动态类加载器很容易),然后进行修复。因此,您可以连接到正在运行的服务器,发现某个函数在短暂中断后未能重新连接到数据库,然后立即重新连接。
正如所有编程结构一样,永远不会有万能药,这种持续部署/开发有一个有趣的副作用:您的程序在内存中可能是正确的,但在磁盘上是错误的。如果您编译了一个函数,然后打破它并保存,则唯一的工作代码副本是正在运行的代码。因此,在保存文件后,重新评估文件非常有用。
看起来很奇特,那么请查看如何在您的生产应用程序中嵌入Clojure REPL

16

我记得有个人来自NASA谈到了他的经历。他的团队在70年代实现了一款宇宙飞船所使用的软件。当发现一些错误时,他们可以远程实时地有效修改软件。

再比如,你想象一下,有一个长时间运行的过程,需要花费数天时间,并且最终结果因为权限或其他小问题无法写入。

还有另外一个例子,在集成阶段需要进行许多小变更,而且变更数量很多。我梦想Java有这样的可能性,因为目前重新构建和重新安装我的应用需要30-40分钟(而重新构建只需10分钟)。


5
那听起来很像远程代理,只不过那是在90年代末。我看过一个关于它的演讲,名为“从一亿五千万英里外调试代码”。我认为更多的是有了REPL(交互式编程环境)而不是能够在系统运行时改变代码的能力挽救了他们,但我可能错了。无论如何,想了解更多细节,请参见http://www.flownet.com/gat/jpl-lisp.html。 - Laurence Gonsalves

9
如果你看一下像 Erlang 这样的东西,其目的是避免停机时间。
它运行在像电话交换机这样的设备上,你不能只是把它关掉几秒钟。
对于更普通的用途,虽然这是一个“好事”,但是可能并不是关键。

2
《Erlang编程语言》一书中有一句很棒的引用:“它是为电话交换机设计的,那里每年几秒钟的停机时间是无法接受的!” - Martin Beckett
1
一个很好的例子是在Erlang The Movie (http://www.youtube.com/watch?v=xrIjfIjssLE)中。看他们如何在不断开电话的情况下部署更改。这也非常有趣。 :) - Steve

7

您可以看到真实的数据,这是一个巨大的优势。因此,您无需进行猜测。


请详细说明。如果您使用真实数据再次运行程序,您将看到真实数据,那么修改已经运行的实例有何帮助? - Laurence Gonsalves
1
@Laurence:根据您所更改的内容,“重新运行”与“在同一图像中逐步更改”可能意味着“完成结果所需的时间要短得多”。如果您必须退出、重新编译,然后从头开始重新启动(可能需要很长时间的)计算,那么您将需要进行完整的运行。但是,如果您在运行图像中具有足够的中间状态,则仅调整最后阶段只需要重新运行最后阶段即可。 - Vatine
如果你想的话,第一天就可以将你的代码投入生产。 :) 查看输入数据并开始调整你的代码以产生正确的输出。我总是进行实时编码。当生命取决于你的打字时,这是一种极好的冲动。 - FlinkmanSV

5

因为你可以吗?

说真的,试用一段时间,当你回到没有REPL的旧编程语言时,你会感到痛苦。

即时反馈,轻松快速测试而不必在测试装置中设置虚假程序状态,能够检查运行程序的状态(那个变量的值是多少)。所有这些都是真正的时间节省者。


3

这主要用于开发中,仅仅是一个时间节省器。

但时间节省器非常重要。

一旦你习惯了它,回到旧的方式就感觉像从飞行转为在焦油中游泳。


2
在工业系统中,这用于PLC编程,以减少停机时间和不安全条件。
这些系统用于核电站、制造系统、钢铁厂等。该过程始终持续运行,停机时间非常昂贵或不安全。想象一下控制核反应堆冷却的系统,您不能关闭该系统以部署新代码,必须能够在其运行时进行修改。
这类似于电话交换系统的Erlang解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接