Как заменить работающую функцию в Common Lisp?

Предположим, мы используем SBCL #'save-lisp-and-die для создания серверного приложения App1, которое работает очень хорошо. Теперь мы хотим заменить функцию #'func1 новой версией, не останавливая App1. Как мы можем сделать это в Common Lisp?

Любое предложение приветствуется!

2 ответа

Решение

Вам нужно загрузить новое определение функции. Тогда новая функция будет доступна немедленно; код вызовет вновь загруженную функцию.

Новое определение функции может быть загружено разными способами:

  • (load (compile-file "file.lisp")) где file.lisp это исходный код для функции
  • (load "file.fasl") где file.fasl скомпилирован исходный код
  • (eval (defun ...))

Конечно, есть исключения и осложнения:

  • Это не заменит уже запущенные вызовы предыдущих функций; например, бесконечный цикл событий не может быть изменен таким образом - он должен поддерживать некоторую остановку и вызов новой функции. Однако такие длительные функции встречаются редко. Это можно обойти, используя рекурсию вместо зацикливания (но не все компиляторы выполняют оптимизацию хвостовых вызовов).
  • Если вы где-то захватили указатель на функцию (например, (function FOO) где FOO это имя функции), она сохранит свое старое значение. Чтобы избежать этого, используйте символы вместо указателей на функции (символы funcallсостояние).
  • Код функции подлежит сборке мусора. Вы должны быть осторожны, чтобы не оставлять ссылки на старые версии функций. Кроме того, если некоторые функции становятся ненужными, вы не должны забывать fmakunbound их символы.
  • Если функция использовалась во время компиляции, весь уязвимый код также должен быть перезагружен
  • Если у вас были высокие уровни оптимизации (что не по умолчанию), компилятор мог бы встроить функцию в другие. CLHS различает случаи, когда переопределение функции становится "неопределенным поведением".

Но на практике перезагрузка кода хорошо работает в большинстве реализаций 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и следуйте инструкциям.

Если вы не хотите делать это по какой-либо причине, ваша реализация может предложить что-то конкретное.

Другие вопросы по тегам