Как заменить работающую функцию в 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
и следуйте инструкциям.
Если вы не хотите делать это по какой-либо причине, ваша реализация может предложить что-то конкретное.