Как перезагрузить файл clojure в REPL - PullRequest
156 голосов
/ 05 октября 2011

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

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

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

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

Ответы [ 8 ]

183 голосов
/ 03 декабря 2013

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

72 голосов
/ 12 апреля 2013

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

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

user=> (refresh)

:reloading (namespace.app)

:ok
55 голосов
/ 22 сентября 2014

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

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

35 голосов
/ 03 августа 2014

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

(require 'my.namespace :reload-all)

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

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

требуется

4 голосов
/ 02 декабря 2015

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

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

4 голосов
/ 24 апреля 2015

Один вкладыш на основании ответа Папачана:

(clojure.tools.namespace.repl/refresh)
2 голосов
/ 05 октября 2011

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

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

0 голосов
/ 03 февраля 2012

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

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

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

...