Ответ 1
Или
(use 'your.namespace :reload)
Каков предпочтительный способ перезагрузки функций, определенных в файле Clojure, без необходимости перезапуска REPL. Прямо сейчас, чтобы использовать обновленный файл, я должен:
src/foo/bar.clj
(load-file "src/foo/bar.clj")
(use 'foo.bar)
Кроме того, (use 'foo.bar :reload-all)
не приводит к требуемому эффекту, который оценивает измененные тела функций и возвращает новые значения, а не ведет себя так, как источник вообще не изменился.
Документация:
Или
(use 'your.namespace :reload)
Существует также альтернатива, использующая tools.namespace, она довольно эффективна:
user=> (use '[clojure.tools.namespace.repl :only (refresh)])
user=> (refresh)
:reloading (namespace.app)
:ok
Перезагрузка Clojure с использованием (require … :reload)
и :reload-all
очень проблематична:
Если вы изменяете два пространства имен, которые зависят друг от друга, вы должны не забудьте перезагрузить их в правильном порядке, чтобы избежать компиляции ошибки.
Если вы удалите определения из исходного файла и затем перезагрузите его, эти определения по-прежнему доступны в памяти. Если другой код зависит от этих определений, он будет продолжать работать, но перерыв при следующем запуске JVM.
Если перезагруженное пространство имен содержит
defmulti
, вы также должны перезагрузить все связанные выраженияdefmethod
.Если перезагруженное пространство имен содержит
defprotocol
, вы также должны перезагрузите любые записи или типы, реализующие этот протокол, и замените любые существующие экземпляры этих записей/типов с новыми экземплярами.Если перезагруженное пространство имен содержит макросы, вы также должны перезагрузить любой пространства имен, которые используют эти макросы.
Если запущенная программа содержит функции, которые закрывают значения в перезагруженное пространство имен, те значения закрытого значения не обновляются. (Это распространено в веб-приложениях, которые строят "обработчик" стек "как состав функций.)
Библиотека clojure.tools.namespace значительно улучшает ситуацию. Он обеспечивает легкую функцию обновления, которая выполняет интеллектуальную перезагрузку на основе графика зависимостей пространств имен.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
К сожалению, перезагрузка второй раз завершится неудачно, если изменилось пространство имен, в котором вы ссылались на функцию refresh
. Это связано с тем, что tools.namespace уничтожает текущую версию пространства имен перед загрузкой нового кода.
myapp.web=> (refresh)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: refresh in this context, compiling:(/private/var/folders/ks/d6qbfg2s6l1bcg6ws_6bq4600000gn/T/form-init819543191440017519.clj:1:1)
Вы можете использовать полное имя var в качестве обходного пути для этой проблемы, но лично я предпочитаю не печатать его при каждом обновлении. Другая проблема с вышеизложенным заключается в том, что после перезагрузки основного пространства имен стандартные вспомогательные функции REPL (например, doc
и source
) больше не упоминаются там.
Чтобы решить эти проблемы, я предпочитаю создавать фактический исходный файл для пространства имен пользователей, чтобы он мог быть надежно перезагружен. Я помещаю исходный файл в ~/.lein/src/user.clj
, но вы можете разместить его в любом месте. Файл должен требовать функцию обновления в объявлении top ns следующим образом:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
Вы можете настроить профиль пользователя leiningen в ~/.lein/profiles.clj
, чтобы местоположение, в которое вы помещали файл, добавляется в путь класса. Профиль должен выглядеть примерно так:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Обратите внимание, что я устанавливаю пространство имен пользователей как точку входа при запуске REPL. Это гарантирует, что вспомогательные функции REPL будут ссылаться в пространстве имен пользователей вместо основного пространства имен вашего приложения. Таким образом, они не потеряются, если вы не измените исходный файл, который мы только что создали.
Надеюсь, это поможет!
Лучший ответ:
(require 'my.namespace :reload-all)
Это не только перезагрузит указанное вами пространство имен, но также перезагрузит все пространства имен зависимостей.
Документация:
Один вкладыш, основанный на ответе папачана:
(clojure.tools.namespace.repl/refresh)
Я использую это в 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 каждого выражения), он сбрасывает все старые определения и дает мне чистую среду. Перед тем, как я начал это делать, я сработал каждые несколько дней по старым определениям, и это спасло мое здравомыслие.:)
Попробуйте загрузить файл снова?
Если вы используете IDE, обычно есть комбинация клавиш для отправки кода-кода в REPL, тем самым эффективно переопределяя связанные функции.
Как только (use 'foo.bar)
работает для вас, это означает, что у вас есть foo/bar.clj или foo/bar_init.class на вашем CLASSPATH. Bar_init.class будет AOT-скомпилированной версией bar.clj. Если вы выполняете (use 'foo.bar)
, я не совсем уверен, что Clojure предпочитает класс над clj или наоборот. Если он предпочтет файлы классов, и у вас есть оба файла, то ясно, что редактирование файла clj, а затем перезагрузка пространства имен не имеет никакого эффекта.
BTW: вам не нужно load-file
перед use
, если ваш CLASSPATH установлен правильно.
BTW2: Если вам нужно использовать load-file
по какой-либо причине, вы можете просто сделать это снова, если вы отредактировали файл.