Как перезагрузить файл clojure в REPL


каков предпочтительный способ перезагрузки функций, определенных в файле Clojure, без перезапуска REPL. Прямо сейчас, чтобы использовать обновленный файл, я должен:

  • edit src/foo/bar.clj
  • закройте REPL
  • откройте REPL
  • (load-file "src/foo/bar.clj")
  • (use 'foo.bar)

кроме того, (use 'foo.bar :reload-all) не приводит к требуемому эффекту, который оценивает измененные тела функций и возвращает новые значения, вместо того чтобы вести себя как источник не изменился.

8 140

8 ответов:

или (use 'your.namespace :reload)

существует также альтернатива, как с помощью инструменты.пространство имен, Это довольно эффективный:

user=> (use '[clojure.tools.namespace.repl :only (refresh)])

user=> (refresh)

:reloading (namespace.app)

:ok

перезагрузка кода Clojure с помощью (require … :reload) и :reload-all и весьма проблематично:

  • если вы измените два пространства имен, которые зависят друг от друга, вы должны не забудьте перезагрузить их в правильном порядке, чтобы избежать компиляции ошибки.

  • если вы удалите определения из исходного файла, а затем перезагрузите его, эти определения все еще доступны в памяти. Если другой код зависит от этих определений, это будет продолжать работать, но будет сломать следующей перезагрузке виртуальной машины.

  • если перезагруженное пространство имен содержит defmulti, вы также должны перезагрузить все связанные defmethod выражения.

  • если перезагруженное пространство имен содержит defprotocol, вы должны также перезагрузите все записи или типы, реализующие этот протокол, и замените любые существующие экземпляры этих записей / типов с новыми экземплярами.

  • если перезагруженное пространство имен содержит макросы, вы также должны перезагрузить любой пространства, которые используют эти макросы.

  • если запущенная программа содержит функции, которые закрываются над значениями в перегруженное пространство имен, эти закрытые значения не обновляются. (Это часто встречается в веб-приложениях, которые строят " обработчик стек " как состав функций.)

clojure.инструменты.библиотека пространств имен улучшает ситуацию значительно. Он обеспечивает простую функцию обновления, которая выполняет интеллектуальную перезагрузку на основе графика зависимостей пространств имен.

myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok

к сожалению перезагрузка во второй раз не удастся, если пространство имен, в котором вы ссылались на

лучший ответ:

(require 'my.namespace :reload-all)

Это не только перезагрузит указанное пространство имен, но и перезагрузит все пространства имен зависимостей.

Я использую это в Lighttable (и awesome instarepl), но он должен использоваться в других инструментах разработки. У меня была такая же проблема со старыми определениями функций и мультиметодов, висящих после перезагрузки, поэтому теперь во время разработки вместо объявления пространств имен с помощью:

(ns my.namespace)

Я объявляю свои пространства имен следующим образом:

(clojure.core/let [s 'my.namespace]
                  (clojure.core/remove-ns s)
                  (clojure.core/in-ns s)
                  (clojure.core/require '[clojure.core])
                  (clojure.core/refer 'clojure.core))

довольно некрасиво, но всякий раз, когда я переоцениваю все пространство имен (Cmd-Shift-Enter в Lighttable, чтобы получить новый instarepl результаты каждого выражения), он сдувает все старые определения и дает мне чистую среду. Я спотыкался каждые несколько дней по старым определениям, прежде чем начал это делать, и это спасло мое здравомыслие. :)

один лайнер, основанный на ответе папачана:

(clojure.tools.namespace.repl/refresh)

попробуйте загрузить файл еще раз?

Если вы используете IDE, обычно есть сочетание клавиш для отправки блока кода в REPL, что эффективно переопределяет связанные функции.

как только (use 'foo.bar) работает для вас, это означает, что у вас есть foo/bar.clj или foo / bar_init.класс на вашем пути к классам. В bar_init.класс будет AOT-скомпилированной версией bar.clj по. Если вы это сделаете (use 'foo.bar), Я не совсем уверен, если Clojure предпочитает класс над clj или наоборот. Если он предпочел бы файлы классов, и у вас есть оба файла, то ясно, что редактирование файла clj, а затем перезагрузка пространства имен не имеет никакого эффекта.

кстати: вам не нужно load-file перед use Если ваш путь к классу установлен правильно.

BTW2: Если вам нужно использовать load-file по причине, то вы можете просто сделать это снова если вы редактировали файл.