Изменить код программы во время работы в Chicken Scheme
Можно ли обновить программный код во время его интерпретации csi, интерпретатором Chicken Scheme? Если так, то как?
Так что я могу в интерактивном режиме изменить часть кода и сразу увидеть последствия этих изменений. Например, предположим, что я написал следующую программу:
(define (loop)
(print "Ciao")
(rest 1)
(loop))
(loop)
(предполагать (rest 1)
имеет эффект приостановки программы на секунду).
Если я запускаю эту программу через csi, она печатает строку "Ciao" каждую секунду. Если я изменяю строку "Ciao" на что-то другое, например, на "else", и сохраняю файл программного кода, то csi продолжает интерпретировать старый программный код, поэтому я постоянно вижу строку "Ciao". В этом случае я хотел бы, чтобы при сохранении измененного кода со строкой "Ciao", замененной на "else", csi продолжал свою работу по интерпретации, просматривая измененный файл вместо старого. Таким образом, в качестве выходных данных я получаю "Ciao", за которым следует "else": "else" начинает появляться, когда я заменяю "Ciao" на "else" в исходном коде.
2 ответа
В общем, ответ "нет". То, как вы должны использовать REPL, - это оценить изменения по частям, а затем оценить функцию или две, чтобы убедиться, что все прошло как ожидалось. Частью этого подхода является структурирование вашей программы таким образом, чтобы ее можно было легко тестировать по частям, что подразумевает отсутствие автоматического запуска бесконечных циклов. В конкретном случае, о котором вы спрашиваете, вы могли бы вместо этого написать
(define (do-stuff)
(print "Ciao"))
(define (main-loop)
(do-stuff)
(rest 1)
(main-loop))
(define (start) (main-loop))
Теперь вы можете постепенно развивать do-stuff
Периодически оценивая новые версии для вашего переводчика и вызывая их, чтобы убедиться, что они работают, затем в конце start
как только вы уверены, что все делаете правильно.
Вы можете получить ответ на этот похожий вопрос о Common Lisp.
Кроме того, если вы использовали Common Lisp и SLIME, теперь вы можете делать более или менее то, что вы предлагали:
(defun do-stuff ()
(format t "Ciao~%"))
(defun main-loop ()
(loop (progn (do-stuff)
(sleep 1))))
(main-loop)
Начиная с этого начнется печать Ciao
на отдельных строках в вашем SLIME REPL. Если вы изменили do-stuff
в
(defun do-stuff ()
(format t "else~%"))
затем ударил его C-c C-c
(привязка по умолчанию для slime-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>
Я не уверен, как сделать то же самое в Схеме, но я достаточно уверен, что это возможно.
Несмотря на это, иногда хочется запустить программу, часть которой бесконечна loop
, Реальным примером может служить тестирование TCP-сервера. Если вы находитесь в такой ситуации, и ваш желаемый рабочий процесс
- Написать файл
- Бежать
csi my-file.scm
- Редактировать файл
- Убийство
csi
- Бежать
csi my-file.scm
goto 3
и вы просто хотите автоматизировать шаги с 4 по 6, для этого вам понадобится внешний инструмент. Взгляни на entr
или же 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!
на это снова.
Возможно, у программистов-курильщиков может быть лучшее решение.