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

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

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

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

Документация:


person pkaleta    schedule 05.10.2011    source источник
comment
(use 'foo.bar :reload-all) всегда отлично работал у меня. Кроме того, (load-file) никогда не понадобится, если ваш путь к классам настроен правильно. Какого желаемого эффекта вы не получаете?   -  person Dave Ray    schedule 05.10.2011
comment
Да какой требуется эффект? Разместите образец bar.clj с подробным описанием необходимого эффекта.   -  person Sridhar Ratnakumar    schedule 05.10.2011
comment
Под требуемым эффектом я имел в виду, что если бы у меня была функция (defn f [] 1) и я изменил ее определение на (defn f [] 2), мне казалось, что после того, как я выдаю (use 'foo.bar :reload-all) и вызываю функцию f, она должна вернуть 2, а не 1. К сожалению, это не работает. для меня, и каждый раз, когда я меняю тело функции, мне приходится перезапускать REPL.   -  person pkaleta    schedule 05.10.2011
comment
У вас должна быть другая проблема в вашей настройке ... :reload или :reload-all должны работать оба.   -  person Jason    schedule 19.02.2016


Ответы (8)


Существует также альтернатива, например, использование tools.namespace, это довольно эффективно:

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

user=> (refresh)

:reloading (namespace.app)

:ok
person papachan    schedule 12.04.2013
comment
этот ответ более правильный - person Bahadir Cambel; 04.12.2014
comment
Предостережение: запуск (refresh), похоже, также заставляет REPL забыть о том, что вам требуется clojure.tools.namespace.repl. Последующие вызовы (refresh) выдадут исключение RuntimeException, невозможно разрешить символ: обновить в этом контексте. Вероятно, лучше всего либо (require 'your.namespace :reload-all), либо, если вы знаете, что вам нужно часто обновлять REPL для данного проекта, создайте профиль :dev и добавьте [clojure.tools.namespace.repl :refer (refresh refresh-all)] в dev/user.clj. - person Dave Yarwood; 03.01.2015
comment
Сообщение в блоге о рабочем процессе Clojure от автора tools.namespace: thinkrelevance.com / blog / 2013/06/04 / clojure-workflow-reloaded - person David Tonhofer; 14.07.2019

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

Надеюсь это поможет!

person Dirk Geurs    schedule 22.09.2014
comment
Хорошие предложения. Один вопрос: почему указанная выше запись: source-paths? - person Alan Thompson; 10.02.2015
comment
ОК, нашел ответ. Файл user.clj должен где-то жить, и хорошее место - это /home/alan/.lein/user.clj (в Linux). Чтобы lein нашел файл, нам нужен файл /home/alan/.lein/profiles.clj с записью вида: ': source-paths [/home/alan/.lein' - person Alan Thompson; 10.02.2015
comment
Собственно говоря, наличие фактического исходного файла позволяет надежно перезагрузить этот файл и пространство имен внутри него. Вот файл, который я сейчас использую: github.com/Dirklectisch /.lein/blob/master/src/user.clj - person Dirk Geurs; 02.03.2015
comment
@ fl00r Извините, я не совсем понял вашу точку зрения. В этом случае это должно быть: source-paths, поскольку мы загружаем исходные файлы, а не другие ресурсы. - person Dirk Geurs; 31.03.2015
comment
@DirkGeurs, с :source-paths я получаю #<FileNotFoundException java.io.FileNotFoundException: Could not locate user__init.class or user.clj on classpath: >, а с :resource-paths все ок. - person fl00r; 31.03.2015
comment
@ fl00r Вы создали исходный файл user.clj? Чтобы решить эти проблемы, я предпочитаю создать фактический исходный файл для пространства имен пользователя, чтобы его можно было надежно перезагрузить. - person Dirk Geurs; 03.04.2015
comment
Да. Теперь он хранится в ~/.lein/user/user.clj вместе с другими вещами. - person fl00r; 03.04.2015
comment
@ fl00r, и он все еще выдает эту ошибку? У вас есть действующий project.clj в папке, из которой вы запускаете REPL? Это может решить вашу проблему. - person Dirk Geurs; 03.04.2015
comment
Да, это довольно стандартно, и все отлично работает с :resource-paths, я нахожусь в моем пользовательском пространстве имен внутри repl. - person fl00r; 03.04.2015
comment
если это так, то clojure repl действительно отстой. - person matanster; 18.05.2017
comment
Я делаю учебник. Ничего страшного, если я reload-all там? - person Alper; 20.12.2019
comment
@Alper Да, с вами, вероятно, все будет в порядке, или, по крайней мере, вы заметите достаточно скоро, если попадете в один из описанных выше случаев. - person Dirk Geurs; 20.12.2019
comment
Я просто отлично провел время, работая с REPL, который лгал мне из-за этой reload проблемы. Потом оказалось, что все, что, как я думал, работало, больше не работает. Может быть, кому-нибудь стоит исправить эту ситуацию? - person Alper; 21.12.2019
comment
@Alper Я не думаю, что они в ближайшее время обратятся к этому на языке. tools.namespace это лучший выбор. Приятно слышать, что вы пробуете Clojure. Там есть за что любить. - person Dirk Geurs; 21.12.2019
comment
Привет! Похоже, что это так, и теперь, когда я получил настройку с Calva на базовом уровне, ситуация улучшается. - person Alper; 22.12.2019
comment
Может tools.namespace поможет как-нибудь переимпортировать и перекомпилированные Java-классы? - person Dima Fomin; 02.07.2020
comment
@ fl00r Кажется, :source-paths действительно вызывает ошибку, когда lein repl вызывается в папке без project.clj (а :resource-paths нет) - person Dima Fomin; 03.07.2020

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

(require 'my.namespace :reload-all)

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

Документация:

требуется

person Alan Thompson    schedule 03.08.2014
comment
Это единственный ответ, который работал с lein repl, Coljure 1.7.0 и nREPL 0.3.5. Если вы новичок в clojure: например, пространство имен ('my.namespace) определяется с помощью (ns ...) в _4 _..._ 5_. - person Aaron Digulla; 25.07.2015
comment
Проблема с этим ответом заключается в том, что исходный вопрос использует (load-file ...), не требуется. Как ей добавить: reload-all в пространство имен после файла загрузки? - person jgomo3; 21.05.2018
comment
Поскольку структура пространства имен, такая как proj.stuff.core, отражает файловую структуру на диске, например src/proj/stuff/core.clj, REPL может найти правильный файл, и вам не нужно load-file. - person Alan Thompson; 21.05.2018

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

(clojure.tools.namespace.repl/refresh)
person Jiezhen Yi    schedule 24.04.2015

Я использую это в Lighttable (и потрясающем 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 для каждого выражения), он сдувает все старые определения и дает мне чистую среду. Каждые несколько дней меня сбивали с толку старые определения, прежде чем я начал это делать, и это спасло мне рассудок. :)

person optevo    schedule 02.12.2015

Попробовать снова загрузить файл?

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

person Paul Lam    schedule 05.10.2011

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

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

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

person Tassilo Horn    schedule 02.02.2012
comment
Не уверен, почему это помечено как правильный ответ. Это не дает четкого ответа на вопрос. - person AnnanFay; 12.06.2013
comment
Как человек, который подходит к этому вопросу, я не нахожу этот ответ очень ясным. - person ctford; 05.09.2013

person    schedule
comment
:reload-all тоже должен работать. OP конкретно говорит, что это не так, но я думаю, что что-то еще не так в среде разработки OP, потому что для одного файла два (:reload и :reload-all) должны иметь одинаковый эффект. Вот полная команда для :reload-all: (use 'your.namespace :reload-all) Это также перезагружает все зависимости. - person Jason; 19.02.2016