在 Chicken Scheme 中 运行 更改程序代码

Change program code while running in Chicken Scheme

Chicken Scheme 解释器 csi 正在解释程序代码时是否可以更新程序代码?如果是,怎么做?

这样我就可以交互式地更改部分代码并立即看到更改的效果。 例如,假设我编写了以下程序:

(define (loop)
   (print "Ciao")
   (rest 1)
   (loop))

(loop)

(假设 (rest 1) 具有暂停程序一秒钟的效果)。

如果我 运行 这个程序,通过 csi,它每秒打印字符串 "Ciao"。如果我把字符串"Ciao"改成别的东西,比如改成"else",然后保存程序代码文件,那么csi继续解释旧的程序代码,所以我不断看到字符串 "Ciao"。我想,在这种情况下,当我用 "else" 替换字符串 "Ciao" 保存修改后的代码时,csi 通过查看继续其解释工作修改后的文件,而不是旧文件。 这样我就得到一些 "Ciao" 和一些 "else" 作为输出:当我在源代码中用 "else" 替换 "Ciao" 时,"else" 开始出现。

一般来说,答案是"don't"。您应该使用 REPL 的方式是评估针对它的零碎更改,然后评估一两个函数以确保一切按预期进行。这种方法的一部分是构建您的程序,以便可以轻松地对其进行分段测试,这意味着不会自动启动任何无限循环。在您询问的特定情况下,您可以改写

(define (do-stuff)
   (print "Ciao"))

(define (main-loop)
   (do-stuff)
   (rest 1)
   (main-loop))

(define (start) (main-loop))

您现在可以逐步开发 do-stuff,定期评估您的解释器的新版本并调用它们以确保它们正常工作,然后在您确信它运行正确后最终调用 start事物。

您可能会从 this similar question 询问有关 Common Lisp 的问题中受益匪浅。

顺便说一句,如果您使用的是 Common Lisp 和 SLIME,您现在可以或多或少地执行您建议的操作:

(defun do-stuff ()
  (format t "Ciao~%"))

(defun main-loop ()
  (loop (progn (do-stuff)
               (sleep 1))))

(main-loop)

启动它会在您的 SLIME REPL 中的不同行上开始打印 Ciao。如果您将 do-stuff 更改为

(defun do-stuff ()
  (format t "else~%"))

然后用 C-c C-cslime-compile-defun 的默认绑定)点击它,你会看到你的 SLIME REPL 开始打印 else 行。

CL-USER> (main-loop)
Ciao
Ciao
Ciao
; compiling (DEFUN DO-STUFF ...)else
else
else
else
else
else
; Evaluation aborted on NIL. User break.
CL-USER> 

我不确定如何在 Scheme 中完成同样的事情,但我有理由相信这是可能的。

综上所述,您有时想要 运行 一个程序部分是无限的 loop。一个真实世界的例子是在测试某种 TCP 服务器时。如果您处于这种情况,并且您想要的工作流程是

  1. 写入文件
  2. 运行 csi my-file.scm
  3. 编辑文件
  4. 杀死csi
  5. 运行 csi my-file.scm
  6. goto 3

而您基本上只想自动执行第 4 步到第 6 步,您需要一个外部工具来为您完成。看一下 entr or hsandbox(后者没有开箱即用的 Scheme 支持,但看起来并不难添加)。

没有通用的方法可以让 运行 程序检查其来源是否有更改,但您似乎可以在 Chicken 中使用足够的功能来推出自己的功能:

(use posix)
(use srfi-18)

(define (watch-reload! file)
  (define (tsleep n)
    (thread-sleep! (seconds->time (+ n (time->seconds (current-time))))))
  (define (get-time)
    (file-modification-time file))
  (thread-start! 
   (lambda ()
     (let loop ((filetime '())) 
       (let ((newtime  (get-time)))
         (when (not (equal? filetime newtime))
           (load file))
         (tsleep 10)
         (loop newtime))))))

现在您所要做的就是使用 watch-reload! 而不是 load,如果文件已被修改,它将每 10 秒检查并重新加载一次。 如果您在文件不是有效方案时保存,它将停止工作,直到您再次对其调用 watch-reload!

可能吃鸡程序员有更好的解决办法