【问题标题】:Editing programs "while they are running"? Why?“在程序运行时”编辑程序?为什么?
【发布时间】:2011-07-01 18:43:11
【问题描述】:

我最近对 ​​Lisp 和 Lispy 语言有了更多的了解,我发现它们非常强大。

我一直在网上阅读的一件事是,使用 Lisp、Clojure 等编写的好处是,您可以“在程序运行时”对其进行编辑。

也许我遗漏了什么,但重点是什么?

当然,它可能会节省几秒钟,但仅此而已吗?每当我对我的程序进行更改时,我都会停止它然后重新启动它,并且几十年来一直运行良好。

除了节省时间之外,肯定还有其他原因——它是什么?

谁能给我一个很好的案例研究,让我对这个功能垂涎三尺? :)

期待流口水!

【问题讨论】:

    标签: clojure lisp scheme


    【解决方案1】:

    有一些非常酷的用例。一个例子是在 GUI 编程中 - 我在实时开发一个 GUI 应用程序时看到了这一点,因为它在我的 Emacs 旁边运行:我为一个新按钮添加了代码,然后点击“Cc Cc”来编译那个单一的功能,按钮只是 出现在窗口中!不必关闭并重新打开应用程序。然后我开始调整小部件并操纵布局,打开的窗口会立即重新排列——按钮会四处移动,新的文本字段会突然出现,等等。只要我执行了我所做的每一个小改动。

    另一个例子是关于 Clojure OpenGL 库“Penumbra”的精彩截屏视频,程序员在其中实时创建 3D 俄罗斯方块游戏。他从 emacs 旁边的一个空的 OpenGL 窗口开始。他定义了一个立方体对象 - C-M-x - 它在屏幕上。运行命令旋转它,它立即开始旋转。运行一个循环,在不同的位置再定义 5 个立方体,它们会出现 pop-pop-pop-pop-pop。这一切都立即响应,完整的 OpenGL 工具包就在那里玩。为您的立方体添加一个新的表面纹理,并立即看到它出现。它变成了一个可延展的 3d 世界 - 代码会动态修改现有世界,而不是在每次更改时关闭并重新打开 3d 画布。

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

    还有一个关于 Clojure 的音频库“Overtone”的精彩演示/截屏视频。该库是一个合成器工具包,您可以在其中拥有一组合成函数来操纵声波。在演示过程中,开发人员编写了一些代码来开始播放音调。然后他花了 10 秒钟编写一个循环,播放该声音 10 次,但每次都使频率更高,然后再次 C-M-x,你会听到它,音符上升得更高。在 20 分钟的时间里,他实时播放了一首歌曲。看起来很有趣。

    Overtone Presentation Link

    其他用途例如:网络爬取/数据挖掘 - 开发和改进用于实时提取信息的算法,查看每一步返回的数据;机器人编程——在机器人运行时向它发送命令;面部/图像识别 - 使用 OpenCV 之类的库,在您开发代码时,您的更改会立即更新库在图像/视频中识别的内容;数学工作(Clojure 有统计的“Incanter”);以及您希望立即查看您的更改对您正在使用的数据产生什么影响的任何环境。

    所以这是在您面前拥有 REPL 的最有趣的方面。那些不是有形的、可延展的、互动的东西开始存在。 GUI 设计、3D 图形、程序化声音制作、提取和转换数据,这些事情通常已经完成了。但是使用 Clojure(在某种程度上也使用其他动态语言),它变得非常有形和直接;编写代码后,您会立即看到每个更改,如果某些内容不起作用或您没有得到预期的结果,您只需更改您错过的内容并立即重新执行。

    Clojure 非常倾向于这样做。疯狂的是,您可以以相同的方式实时使用 Java 库——尽管 Java 本身不能!所以 Overtone 正在实时使用 Java 合成器库,尽管事实上你在 Java 中永远无法做到,Penumbra 正在使用 Java OpenGL 绑定等。这是因为 Rich Hickey 设计了 ​​Clojure,因此它可以即时编译为 JVM 字节码。这是一门了不起的语言 - Clojure 为编程变得非常有趣和高效做出了巨大贡献。

    【讨论】:

    • 关于你答案的第一段,你使用了哪个 GUI 工具包?
    • Matthias,我当时实际上是在 Common Lisp 中与 McCLIM 一起工作。在 Clojure 中,我相信人们已经弄清楚了如何使用 Swing 来实现。我认为对于您使用的大多数 GUI 工具包,如果您评估代码然后将鼠标移到窗口上,它将触发重绘并显示小部件;还有一些技巧可以让重绘发生而无需费心去拿鼠标。
    • 好的,我添加了指向 Penumbra 和 Overtone 演示文稿的链接。我还添加了一个指向交互式编程帖子的链接,其中包括各种截屏视频,包括底部的一对与 Clojure 相关的视频。
    • 我刚看了 Overtone 的演示:最后 15 分钟的声音很乱(它从谈话开始播放音频,所以你听不到他们在说什么)。
    • Clojure Swing 包装器名为 Seesaw。那里有同样的经历。
    【解决方案2】:

    除了节省时间之外,肯定还有其他原因——它是什么?

    不,没有。我的意思是,从不是:使用计算机的全部原因是为了节省时间。没有什么是电脑能做的,你不能用手做。只是需要更长的时间。

    在这种情况下,我不会忽略“几秒钟”,因为在我的整个编程生涯中,这是我整天做的最多的事情之一。几秒钟重新编译,几秒钟重新运行,几秒钟重新创建我的程序上次的状态——即使在快速的工作站上,迭代之间也很容易间隔一分钟。 (它曾经更糟糕,但更快的硬件只会让它变得不那么糟糕,不好。整个文件或更糟糕的重新编译是 I/O 绑定的,并且可能永远不会*匹配更精细编译的速度。)

    在 Lisp 中,在已经运行的进程中重新编译单个函数几乎是瞬间完成的(我什至从未见过 0.1 秒,即使在我使用 5 年的笔记本电脑上也是如此),并且重新启动意味着我不必重新创建我的状态,即使有信号。

    这是一个工具,它可以让我将我作为程序员所做的最慢和最常见的事情之一的速度提高 100 倍以上。我不知道你还需要什么。我们可能可以编造一些原因,但如果这还不够,我不知道会是什么。嗯,它也很酷? :-)

    (* 每当有人对涉及技术的事情说“从不”时,那个人总是在 2 年后看起来像个彻头彻尾的白痴,尽管 Lisp 的寿命很长,但我肯定也不例外。)

    【讨论】:

    • 现在是 2015 年,您所说的仍然是正确的。你还不是个白痴……但 ;)
    【解决方案3】:

    Lisp 有一个营销口号:

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

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

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

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

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

    在 Lisp 系统中,您更改 CAD 系统中的类,然后它可以立即激活。 当人们问,如果 Lisp 为大型软件团队工作,答案可能是,如果您以增量方式工作,那么大型软件团队不是必需的。那么问题是/是熟悉增量开发的真正熟练的软件开发人员很少(是?)。

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

    【讨论】:

      【解决方案4】:

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

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

      *不保证。


      对我来说,我怀疑这里的其他一些人这种 REPL 驱动的开发 的真正好处是它可以非常有趣。 甚至令人上瘾。有时它真的可以给人一种制作代码的感觉。试一试...来吧,伙计试试看,第一个 REPL 总是免费的 :)


      这些天来的一大吸引力是持续部署。

      目前持续部署的想法是,您更改一件事,构建所有内容(或者打包它)然后部署。使用 lisp 模型,实际上可以在部署时编辑已部署的(通常是接收真实客户会话镜像的框)框。

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


      另一个令人垂涎的观点是获得安全修复的好处而不必声明任何停机时间的想法。您可以进行升级,而不会花费您的 SLA 任何宝贵的“计划停机时间”。如果你必须提前六个月安排计划好的停机时间,而你只能得到两个小时的时间(对于这些可怜的人来说),那真的会让他们流口水。
      如果您在部署时对正在运行的应用程序具有 repl 访问权限(可能(有权限)在客户站点上),您可以在应用程序运行时连接到应用程序并在现有代码在现有上下文中运行测试 无需停止并连接调试器。您也不会从调试器中获得任何速度损失。在没有 REPL 的情况下也可以做到这一点,但是当您在其中获得 repl 时,您可以轻松地创建新代码(有人会说通过调试器注入动态类加载器很容易)然后修复问题。因此,您可以连接到正在运行的服务器。发现某个函数在短暂中断后无法重新连接到数据库,然后立即重新连接。


      与所有编程结构一样,永远不会是灵丹妙药,这种持续的部署/开发有一个有趣的缺点:您的程序在内存中可能是正确的,而在磁盘上可能是错误的。如果您编译一个函数然后将其中断并保存,那么代码的唯一工作副本就是正在运行的那个。在保存文件后,我知道这一点并重新评估文件没有用。
      这可能听起来很奇怪,所以去看看如何Embed a Clojure REPL in your production application

      【讨论】:

        【解决方案5】:

        我记得 NASA 的某个人描述了他的经历。早在 70 年代,他的团队就实现了宇宙飞船中使用的软件。当发现一些错误时,他们有效地远程修改了他们的软件。

        或者假设您有一个需要数天才能执行的漫长过程,最后由于权限或其他小问题而无法写入结果。

        又一个例子。你正处于集成阶段,你必须做很多小的改变。还有很多。我梦想在 Java 中实现这种可能性,因为目前我需要 30-40 分钟来重建和重新安装我的应用程序(在 10 分钟内再次重建它)。

        【讨论】:

        • 这听起来很像远程代理,除了那是在 90 年代后期。我看到一个关于这个副标题的演讲“从 1.5 亿英里外调试代码”。我认为更多的是 repl 的存在,而不是在系统运行时更改代码的能力来保存它们,但我可能弄错了。无论如何,有关更多详细信息,请参阅flownet.com/gat/jpl-lisp.html
        【解决方案6】:

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

        它在手机开关之类的东西上运行,你不能只关掉几秒钟。

        不过,对于更正常的用途,这是一个“很高兴拥有”的功能,但是是的,可能并不重要。

        【讨论】:

        • Erlang 书中的精彩引述。 “它专为无法接受每年几秒钟的停机时间的电话交换机而设计”!
        • Erlang The Movie (youtube.com/watch?v=xrIjfIjssLE) 就是一个很好的例子。看着他们在不挂断电话的情况下部署对电话交换机的更改。看的也蛮好笑的。 :)
        【解决方案7】:

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

        【讨论】:

        • 请详细说明。如果您再次使用真实数据运行程序,您会看到真实数据,那么修改已经运行的实例有什么帮助?
        • @Laurence:根据您的更改,“重新运行”与“在同一图像中增量更改”可能意味着“完成结果的时间要短得多”。如果您必须退出、重新编译,然后从头开始(可能很长)计算,您将获得完整运行的顺序。但是如果你在运行的镜像中有足够的中间状态,调整最后一个阶段只需要重新运行最后一个阶段。
        • 我想,您可以在第一天将您的代码投入生产。 :) 查看传入数据并开始调整代码以产生正确的输出。我总是做实时编码。当生命取决于您输入的内容时,这真是太匆忙了。
        【解决方案8】:

        因为你可以?

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

        即时反馈,无需在测试夹具中设置虚假程序状态即可轻松进行快速测试,能够检查运行程序的状态(该变量的值是什么)。所有这些都可以真正节省时间。

        【讨论】:

          【解决方案9】:

          它主要用于开发,只是节省时间。

          但是节省时间非常重要。

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

          【讨论】:

            【解决方案10】:

            在工业系统中,这用于 PLC 编程,以减少停机时间和不安全情况。

            这些系统用于核电站、制造系统、钢厂等。该过程始终在持续运行,停机时间非常昂贵或不安全。想象一个控制核反应堆冷却的系统,您不能关闭该系统来部署新代码,您必须能够在它运行时对其进行修改。

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

            【讨论】:

              【解决方案11】:

              好吧,假设您需要修补服务器并且停止它。

              如果你用一种“典型”的语言来做这件事,那将涉及一些沉重的魔法。您必须在执行代码的“背后”摸索。我认为它需要修补函数表等等,所有这些都在组装和操作函数指针。臭虫的好地方。

              在 Lisp 中,语言模型中内置了无需停机即可更新的理念。虽然存在一些您无法摆脱的更新复杂性(如何处理长时间运行的连接),但它不需要编译语言的强大魔力。

              虽然我没有花大量时间在它上面(即任何有用的东西),但我确实在 Common Lisp 中设计了一个服务器原型,它至少可以在网络上进行一些实时修补,而无需停机时间。

              【讨论】:

                【解决方案12】:

                除了即时修改程序而无需重新启动所有程序(已经做了几十年并不意味着它是最好的事情,对吧?)之外,另一件好事是您可以在当前的程序中检查您的程序状态并能够弄清楚发生了什么。

                【讨论】:

                • 这似乎不是一个答案。也许作为评论更好?
                【解决方案13】:

                Casey Muratori 刚刚就如何使用 C 和 Microsoft 的 C/C++ 编译器进行了一些课程。其实很简单,就几十行代码。查看视频 22/24/25:

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

                在游戏设计中,基本原理是能够更快地调整常数以找到您所瞄准的情感基调。诸如游戏感觉、非玩家行为脚本和场景照明/环境之类的东西会从中受益匪浅。

                【讨论】:

                  猜你喜欢
                  • 2012-03-20
                  • 1970-01-01
                  • 2012-08-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-08-31
                  • 2011-03-21
                  • 2018-12-31
                  • 2016-02-25
                  相关资源
                  最近更新 更多