web-dev-qa-db-ja.com

REPLでclojureファイルを再ロードする方法

REPLを再起動することなく、Clojureファイルで定義された関数を再ロードする好ましい方法は何ですか。現在、更新されたファイルを使用するには、次の手順を実行する必要があります。

  • 編集src/foo/bar.clj
  • rEPLを閉じます
  • rEPLを開く
  • (load-file "src/foo/bar.clj")
  • (use 'foo.bar)

さらに、(use 'foo.bar :reload-all)は、ソースがまったく変更されていないように動作するのではなく、関数の変更された本体を評価して新しい値を返すという、必要な効果をもたらしません。

ドキュメント:

156
pkaleta

または(use 'your.namespace :reload)

183
Ming

tools.namespace を使用するような代替手段もあります。これは非常に効率的です。

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

user=> (refresh)

:reloading (namespace.app)

:ok
72
papachan

(require … :reload)および:reload-allを使用したClojureコードの再読み込みは 非常に問題の多い

  • 相互に依存する2つのネームスペースを変更する場合、コンパイルエラーを回避するために正しい順序でリロードすることを忘れないでください。

  • ソースファイルから定義を削除してから再ロードしても、それらの定義はメモリ内で使用可能です。他のコードがこれらの定義に依存している場合、それは機能し続けますが、次にJVMを再起動すると壊れます。

  • 再ロードされた名前空間にdefmultiが含まれる場合、関連するdefmethod式もすべて再ロードする必要があります。

  • 再ロードされた名前空間にdefprotocolが含まれる場合、そのプロトコルを実装するレコードまたはタイプも再ロードし、それらのレコード/タイプの既存のインスタンスを新しいインスタンスに置き換える必要があります。

  • 再ロードされた名前空間にマクロが含まれている場合、それらのマクロを使用する名前空間も再ロードする必要があります。

  • 実行中のプログラムに、リロードされたネームスペースの値を閉じる関数が含まれている場合、それらの閉じられた値は更新されません。 (これは、関数の構成として「ハンドラースタック」を構築するWebアプリケーションで一般的です。)

Clojure.tools.namespaceライブラリは状況を大幅に改善します。名前空間の依存関係グラフに基づいてスマートな再読み込みを行う簡単な更新機能を提供します。

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

残念ながら、refresh関数を参照したネームスペースが変更された場合、2回目の再読み込みは失敗します。これは、新しいコードをロードする前に、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)

この問題の回避策として完全修飾変数名を使用できますが、個人的には、リフレッシュのたびにそれを入力する必要はありません。上記の別の問題は、メイン名前空間をリロードした後、標準のREPLヘルパー関数(docsourceなど)が参照されなくなることです。

これらの問題を解決するには、ユーザー名前空間の実際のソースファイルを作成して、確実に再読み込みできるようにします。ソースファイルを~/.lein/src/user.cljに配置しましたが、どこにでも配置できます。このファイルでは、次のように、トップns宣言に更新機能が必要です。

(ns user
  (:require [clojure.tools.namespace.repl :refer [refresh]]))

~/.lein/profiles.cljleiningenユーザープロファイル をセットアップすると、ファイルを配置した場所がクラスパスに追加されます。プロファイルは次のようになります。

{:user {:dependencies [[org.clojure/tools.namespace “0.2.7”]]
        :repl-options { :init-ns user }
        :source-paths [“/Users/me/.lein/src”]}}

REPLを起動するときに、ユーザー名前空間をエントリポイントとして設定することに注意してください。これにより、REPLヘルパー関数がアプリケーションのメイン名前空間ではなくユーザー名前空間で参照されるようになります。そうすれば、作成したソースファイルを変更しない限り、それらは失われません。

お役に立てれば!

55
Dirk Geurs

最良の答えは:

(require 'my.namespace :reload-all)

これは、指定されたネームスペースをリロードするだけでなく、すべての依存ネームスペースもリロードします。

ドキュメント:

必須

36
Alan Thompson

私はこれを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))

かなりいですが、名前空間全体(LighttableでCmd-Shift-Enterを使用して各式の新しいinstareplの結果を取得する)を再評価すると、古い定義がすべて吹き飛ばされ、クリーンな環境が得られます。私はこれを始める前に古い定義に数日ごとにつまずき、それが私の正気を救った。 :)

4
optevo

Papachanの答えに基づくライナー:

(clojure.tools.namespace.repl/refresh)
4
Jiezhen Yi

ロードファイルを再試行しますか?

IDEを使用している場合、通常、コードブロックをREPLに送信するためのキーボードショートカットがあり、関連する機能を効果的に再定義します。

2
Paul Lam

(use 'foo.bar)が機能するとすぐに、CLASSPATHにfoo/bar.cljまたはfoo/bar_init.classがあることを意味します。 bar_init.classは、bar.cljのAOTコンパイルバージョンです。 (use 'foo.bar)を実行する場合、Clojureがcljよりもクラスを好むのか、それとも逆の方法を好むのか、正確にはわかりません。クラスファイルが優先され、両方のファイルがある場合は、cljファイルを編集して名前空間を再ロードしても効果がないことは明らかです。

ところで:CLASSPATHが適切に設定されている場合、useの前にload-fileを指定する必要はありません。

BTW2:何らかの理由でload-fileを使用する必要がある場合、ファイルを編集した場合は、もう一度それを行うことができます。

0
Tassilo Horn