如何替换Common Lisp中正在运行的函数?

如何替换Common Lisp中正在运行的函数?,lisp,common-lisp,sbcl,Lisp,Common Lisp,Sbcl,假设我们使用SBCL的#'save-lisp-and-die来创建一个服务器应用程序App1,它工作得非常好。现在我们想用新版本替换函数#'func1,而不停止App1。我们如何在Common Lisp中实现这一点 任何建议都将不胜感激 我个人确保SWANK(SLIME的服务器部分)正在运行,这样我就可以随时使用Emacs+SLIME连接到映像并重新定义我想要的内容 (ql:quickload "swank") (swank:start-server :port 1234) ;; listen

假设我们使用SBCL的#'save-lisp-and-die来创建一个服务器应用程序App1,它工作得非常好。现在我们想用新版本替换函数#'func1,而不停止App1。我们如何在Common Lisp中实现这一点


任何建议都将不胜感激

我个人确保SWANK(SLIME的服务器部分)正在运行,这样我就可以随时使用Emacs+SLIME连接到映像并重新定义我想要的内容

(ql:quickload "swank")
(swank:start-server :port 1234) ;; listen for SLIME connections on port 1234
然后在Emacs中,您可以
M-x slime connect
,并按照提示操作


如果您出于任何原因不想这样做,您的实现可能会提供一些特定的功能。

您需要加载新的函数定义。然后新功能将立即可用;代码将调用新加载的函数

可以通过多种方式加载新函数定义:

  • (加载(编译文件“file.lisp”)
    其中
    file.lisp
    是函数的源代码
  • (加载“file.fasl”)
    其中
    file.fasl
    是编译源代码
  • (eval(defun…)
当然,也有例外和复杂情况:

  • 这不会替换先前函数的已运行调用;例如,无限事件循环不能以这种方式更改-它必须支持某种类型的停止和调用新函数。但是,这种长时间运行的函数很少。它可以通过使用递归而不是循环来解决(但并非所有编译器都进行尾部调用优化)
  • 如果您在某处抓取了指向函数的指针(例如,通过
    (function FOO)
    ,其中
    FOO
    是函数的名称),它将保留其旧值。为了避免这种情况,请使用符号而不是函数指针(符号是
    funcall
    able)
  • 函数的代码是垃圾收集的对象。您应该小心不要将对函数旧版本的引用放在周围。此外,如果某些函数不再需要,您不应该忘记
    fmakunbound
    它们的符号
  • 如果函数是在编译时使用的,那么所有受影响的代码也应该重新加载
  • 如果您有高级别的优化(默认情况下不是这样),编译器可能已经将该函数内联到其他函数中。当重新定义函数成为“未定义的行为”时,CLHS会区分这些情况

但实际上,,代码重新加载在大多数常见的Lisp实现中效果很好。

您的意思是我们应该在发布的映像中包含swank吗?您还可以查看一个目录,查找新的修补程序文件,然后在程序运行时将其加载替换功能。我认为在发布的映像中添加swank没有问题,如果您真的希望能够在生产时对其进行热修补。只要确保端口安全就行了。@z_axis:似乎是ticket@JustinMegawarne:我已经玩过emacs和sbcl:
(生成线程(lambda()(swank:start server“dfghfgh”))
。现在我有了一个带有swank和repl缓冲区的图像。我还可以从emacs连接到它,并在repl缓冲区(emacs和terminal)上重新定义函数。现在我想知道你为什么不建议把swank放在另一个线程上?我能猜到的唯一合理解释是,您在单独的线程中运行软件。我说的对吗?请澄清:您是指在运行的lisp程序中替换函数,还是指在调用函数时替换函数(例如,在某个长循环中)?我指的是在运行的lisp程序中替换函数,该函数可能正在运行,也可能不在运行。