Come ricaricare un file clojure in REPL


170

Qual è il modo preferito di ricaricare le funzioni definite in un file Clojure senza riavviare REPL. In questo momento, per utilizzare il file aggiornato devo:

  • modificare src/foo/bar.clj
  • chiudere il REPL
  • aprire la REPL
  • (load-file "src/foo/bar.clj")
  • (use 'foo.bar)

Inoltre, (use 'foo.bar :reload-all)non si ottiene l'effetto richiesto, che sta valutando i corpi modificati delle funzioni e restituendo nuovi valori, invece di comportarsi come l'origine non è cambiata affatto.

Documentazione:


20
(use 'foo.bar :reload-all)ha sempre funzionato bene per me. Inoltre, (load-file)non dovrebbe mai essere necessario se il tuo percorso di classe è impostato correttamente. Qual è l '"effetto richiesto" che non ottieni?
Dave Ray,

Sì, qual è l '"effetto richiesto"? Pubblica un campione bar.cljdettagliato sull'effetto richiesto.
Sridhar Ratnakumar,

1
Per effetto richiesto intendevo dire che se avessi una funzione (defn f [] 1)e ne avessi cambiato la definizione (defn f [] 2), mi sembrava che dopo aver emesso (use 'foo.bar :reload-all)e chiamato la ffunzione dovesse restituire 2, non 1. Sfortunatamente non funziona così per me e per tutti volta che cambio il corpo della funzione devo riavviare il REPL.
pkaleta,

Devi avere un altro problema nella configurazione ... :reloado :reload-allentrambi dovrebbero funzionare.
Jason,

Risposte:


196

O (use 'your.namespace :reload)


3
:reload-alldovrebbe anche funzionare. L'OP dice specificamente di no, ma penso che ci fosse qualcos'altro nell'ambiente di sviluppo dell'OP perché per un singolo file i due ( :reloade :reload-all) dovrebbero avere lo stesso effetto. Ecco il comando completo per :reload-all: (use 'your.namespace :reload-all) Ricarica anche tutte le dipendenze.
Jason,

77

Esiste anche un'alternativa come l'utilizzo di tools.namespace , è piuttosto efficiente:

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

user=> (refresh)

:reloading (namespace.app)

:ok

3
questa risposta è più appropriata
Bahadir Cambel,

12
Avvertenza: la corsa (refresh)sembra anche far dimenticare al REPL che hai richiesto clojure.tools.namespace.repl. Le chiamate successive a (refresh)ti daranno una RuntimeException, "Impossibile risolvere il simbolo: aggiornare in questo contesto." Probabilmente la cosa migliore da fare è o (require 'your.namespace :reload-all), o, se sai che vorrai aggiornare molto il tuo REPL per un determinato progetto, creare un :devprofilo e aggiungerlo [clojure.tools.namespace.repl :refer (refresh refresh-all)]adev/user.clj .
Dave Yarwood,

1
Blog post sul flusso di lavoro Clojure dell'autore di tools.namespace: thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded
David Tonhofer

61

Ricaricare il codice Clojure usando (require … :reload)ed :reload-allè molto problematico :

  • Se si modificano due spazi dei nomi che dipendono l'uno dall'altro, è necessario ricordarsi di ricaricarli nell'ordine corretto per evitare errori di compilazione.

  • Se si rimuovono le definizioni da un file di origine e lo si ricarica, tali definizioni sono ancora disponibili in memoria. Se altro codice dipende da tali definizioni, continuerà a funzionare ma si interromperà al successivo riavvio di JVM.

  • Se lo spazio dei nomi ricaricato contiene defmulti, è inoltre necessario ricaricare tutte le defmethodespressioni associate .

  • Se lo spazio dei nomi ricaricato contiene defprotocol, è inoltre necessario ricaricare tutti i record o tipi che implementano quel protocollo e sostituire eventuali istanze esistenti di tali record / tipi con nuove istanze.

  • Se lo spazio dei nomi ricaricato contiene macro, è necessario ricaricare anche gli spazi dei nomi che utilizzano tali macro.

  • Se il programma in esecuzione contiene funzioni che chiudono i valori nello spazio dei nomi ricaricato, tali valori chiusi non vengono aggiornati. (Questo è comune nelle applicazioni web che costruiscono lo "stack gestore" come una composizione di funzioni.)

La libreria clojure.tools.namespace migliora significativamente la situazione. Fornisce una funzione di aggiornamento facile che esegue un ricaricamento intelligente basato su un grafico delle dipendenze degli spazi dei nomi.

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

Sfortunatamente il ricaricamento una seconda volta fallirà se lo spazio dei nomi in cui si fa riferimento alla refreshfunzione è cambiato. Ciò è dovuto al fatto che tools.namespace distrugge la versione corrente dello spazio dei nomi prima di caricare il nuovo codice.

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)

È possibile utilizzare il nome var completo come soluzione alternativa per questo problema, ma personalmente preferisco non doverlo digitare ad ogni aggiornamento. Un altro problema con quanto sopra è che dopo aver ricaricato lo spazio dei nomi principale le funzioni di supporto REPL standard (come doce source) non vengono più citate lì.

Per risolvere questi problemi, preferisco creare un file sorgente effettivo per lo spazio dei nomi utente in modo che possa essere ricaricato in modo affidabile. Ho inserito il file sorgente ~/.lein/src/user.cljma puoi inserirlo ovunque. Il file dovrebbe richiedere la funzione di aggiornamento nella dichiarazione ns in alto in questo modo:

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

È possibile impostare un profilo utente leiningen in ~/.lein/profiles.cljmodo che la posizione in cui è stato inserito il file sia aggiunta al percorso della classe. Il profilo dovrebbe assomigliare a questo:

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

Si noti che ho impostato lo spazio dei nomi utente come punto di ingresso all'avvio di REPL. Ciò garantisce che le funzioni di supporto REPL vengano referenziate nello spazio dei nomi utente anziché nello spazio dei nomi principale dell'applicazione. In questo modo non andranno persi se non si modifica il file di origine che abbiamo appena creato.

Spero che questo ti aiuti!


Buoni suggerimenti. Una domanda: perché la voce ": percorsi-origine" sopra?
Alan Thompson,

2
@DirkGeurs, con :source-pathsottengo #<FileNotFoundException java.io.FileNotFoundException: Could not locate user__init.class or user.clj on classpath: >, mentre con :resource-pathstutto è ok.
fl00r

1
@ fl00r e genera ancora questo errore? Hai un project.clj valido nella cartella da cui stai avviando il REPL? Questo potrebbe risolvere il tuo problema.
Dirk Geurs,

1
Sì, è piuttosto standard e tutto funziona bene :resource-paths, sono nel mio spazio dei nomi utente all'interno di repl.
fl00r,

1
Mi sono appena divertito a lavorare con un REPL che mi mentiva a causa di questo reloadproblema. Poi si è scoperto che tutto quello che pensavo funzionasse non era più. Forse qualcuno dovrebbe risolvere questa situazione?
Alper il

41

La migliore risposta è:

(require 'my.namespace :reload-all)

Questo non solo ricaricherà lo spazio dei nomi specificato, ma ricaricherà anche tutti gli spazi dei nomi delle dipendenze.

Documentazione:

richiedere


2
Questa è l'unica risposta con cui ha funzionato lein repl, Coljure 1.7.0 e nREPL 0.3.5. Se non conosci il clojure: lo spazio dei nomi ( 'my.namespace) è definito con (ns ...)in src/... /core.clj, ad esempio.
Aaron Digulla,

1
Il problema con questa risposta è che la domanda originale sta usando (caricamento file ...), non è necessario. Come può aggiungere: ricaricare tutto allo spazio dei nomi dopo il caricamento del file?
jgomo3,

Poiché la struttura dello spazio dei nomi come proj.stuff.corerispecchia la struttura dei file sul disco src/proj/stuff/core.clj, la REPL può individuare il file corretto e non è necessario load-file.
Alan Thompson,

6

Una fodera basata sulla risposta di papachan:

(clojure.tools.namespace.repl/refresh)

5

Lo uso in Lighttable (e nel fantastico instarepl) ma dovrebbe essere utile in altri strumenti di sviluppo. Stavo avendo lo stesso problema con vecchie definizioni di funzioni e multimetodi in giro dopo le ricariche così ora durante lo sviluppo invece di dichiarare spazi dei nomi con:

(ns my.namespace)

Dichiaro i miei spazi dei nomi in questo modo:

(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))

Abbastanza brutto ma ogni volta che rivaluto l'intero spazio dei nomi (Cmd-Shift-Enter in Lighttable per ottenere i nuovi risultati instarepl di ogni espressione), cancella tutte le vecchie definizioni e mi dà un ambiente pulito. Sono stato inciampato ogni pochi giorni da vecchie definizioni prima di iniziare a fare questo e mi ha salvato la sanità mentale. :)


3

Prova di nuovo a caricare il file?

Se stai usando un IDE, di solito c'è una scorciatoia da tastiera per inviare un blocco di codice al REPL, ridefinendo così efficacemente le funzioni associate.


1

Non appena (use 'foo.bar)funziona per te, significa che hai CLASSEFATH foo / bar.clj o foo / bar_init.class. Bar_init.class sarebbe una versione compilata AOT di bar.clj. Se lo fai (use 'foo.bar), non sono esattamente sicuro che Clojure preferisca la classe al clj o viceversa. Se preferisce i file di classe e hai entrambi i file, è chiaro che la modifica del file clj e il ricaricamento dello spazio dei nomi non ha alcun effetto.

A proposito: non è necessario load-fileprima useche CLASSPATH sia impostato correttamente.

BTW2: se devi usarlo load-fileper un motivo, puoi semplicemente farlo di nuovo se hai modificato il file.


14
Non sono sicuro del perché questa sia contrassegnata come la risposta corretta. Non risponde chiaramente alla domanda.
AnnanFay,

5
Come qualcuno che arriva a questa domanda, non trovo questa risposta molto chiara.
CTF,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.