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


170

REPLを再起動せずにClojureファイルで定義された関数を再ロードするための推奨される方法は何ですか。現在、更新されたファイルを使用するには、次のことを行う必要があります。

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

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

ドキュメンテーション:


20
(use 'foo.bar :reload-all)私はいつもうまくいきました。また、(load-file)クラスパスが正しく設定されている場合は必要ありません。あなたが得ていない「必要な効果」は何ですか?
Dave Ray

はい、「必要な効果」とは何ですか?bar.clj「必要な効果」について詳しく説明したサンプルを投稿してください。
Sridhar Ratnakumar、2011年

1
必要な効果とは、関数が(defn f [] 1)あり、その定義をに変更した場合、関数(defn f [] 2)を発行(use 'foo.bar :reload-all)して呼び出した後f、1ではなく2を返すように思われることを意味しました関数の本体を変更したときにREPLを再起動する必要があります。
pkaleta

設定に別の問題がある必要があります... :reloadまたは:reload-all両方が機能するはずです。
Jason

回答:


196

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


3
:reload-allも動作するはずです。OPは具体的にはそうではないと述べていますが、単一のファイルに対して2つのファイル(:reload:reload-all)が同じ効果を持つはずなので、OPの開発環境に何か他の問題があったと思います。 これが完全なコマンドです:reload-all(use 'your.namespace :reload-all)これもすべての依存関係をリロードします。
Jason

77

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

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

user=> (refresh)

:reloading (namespace.app)

:ok

3
この答えはより適切です
バハディールカンベル

12
警告:実行する(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です。
Dave Yarwood、2015年

1
tools.namespaceの作成者によるClojureワークフローに関するブログ投稿:thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded
David Tonhofer

61

使ってClojureのコードをリロード(require … :reload)して:reload-allいる非常に問題

  • 相互に依存する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)

この問題の回避策として完全修飾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ヘルパー関数がアプリケーションのメイン名前空間ではなくユーザー名前空間で参照されるようになります。そうすれば、先ほど作成したソースファイルを変更しない限り、ファイルが失われることはありません。

お役に立てれば!


良い提案。1つの質問:なぜ上記の ":source-paths"エントリですか?
アラントンプソン、

2
@DirkGeurs、:source-paths私はを取得#<FileNotFoundException java.io.FileNotFoundException: Could not locate user__init.class or user.clj on classpath: >:resource-pathsますが、すべてでOKです。
fl00r 2015年

1
@ fl00rとそれはまだそのエラーをスローしますか?REPLを起動するフォルダーに有効なproject.cljがありますか?それで問題が解決するかもしれません。
Dirk Geurs、2015

1
はい、それはかなり標準的で、すべてで正常に動作し:resource-pathsます。私はrepl内のユーザー名前空間にいます。
fl00r 2015

1
私はこのreload問題のために私に嘘をついていたREPLを扱う素晴らしい時間を過ごしました。それから、私が働いていると思っていたすべてがもうないということがわかりました。多分誰かがこの状況を修正する必要がありますか?
Alper

41

最良の答えは:

(require 'my.namespace :reload-all)

これにより、指定した名前空間が再読み込みされるだけでなく、すべての依存性名前空間も再読み込みされます。

ドキュメンテーション:

必要とする


2
これはlein repl、Coljure 1.7.0およびnREPL 0.3.5 で機能した唯一の回答です。clojureを初めて使用する場合:名前空間('my.namespace)は、たとえば... で定義さ(ns ...)src/ます/core.clj
アーロンディグラ

1
この回答の問題は、元の質問が(load-file ...)を使用していることであり、必須ではありません。ロードファイルの後にネームスペースに:reload-allを追加するにはどうすればよいですか?
jgomo3

のような名前空間構造はのようなproj.stuff.coreディスク上のファイル構造をsrc/proj/stuff/core.cljミラーリングするため、REPLは正しいファイルを見つけることができ、ユーザーは必要ありませんload-file
アラントンプソン


5

私はこれを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結果を取得する)を再評価すると、古い定義がすべて削除され、クリーンな環境が得られます。私はこれを始める前に古い定義に数日おきにつまずいて、それは私の正気を救いました。:)


3

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

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


1

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

ところで:CLASSPATHが適切に設定されload-fileuseいれば、前に行う必要はありません。

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


14
これが正解としてマークされている理由がわかりません。それは質問にはっきりと答えません。
AnnanFay 2013年

5
誰かがこの質問をしているので、この答えはあまり明確ではありません。
ctford 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.