Comment recharger un fichier clojure dans REPL

Quelle est la manière préférée de recharger les fonctions définies dans un fichier Clojure sans avoir à redémarrer la REPL. En ce moment, pour utiliser le fichier mis à jour, je dois:

  • éditer src/foo/bar.clj
  • fermer le REPL
  • ouvrir le REPL
  • (load-file "src/foo/bar.clj")
  • (use 'foo.bar)

De plus, (use 'foo.bar :reload-all) n’entraîne pas d’effet requirejs, ce qui évalue les corps modifiés des fonctions et renvoie de nouvelles valeurs, au lieu de se comporter car la source n’a pas du tout changé.

Ou (use 'your.namespace :reload)

Il y a aussi une alternative comme utiliser tools.namespace , c’est plutôt efficace:

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

Recharger le code Clojure en utilisant (require … :reload) et :reload-all est très problématique :

  • Si vous modifiez deux espaces de noms qui dépendent les uns des autres, n’oubliez pas de les recharger dans le bon ordre pour éviter les erreurs de compilation.

  • Si vous supprimez des définitions d’un fichier source et que vous le rechargez, ces définitions sont toujours disponibles en mémoire. Si un autre code dépend de ces définitions, il continuera à fonctionner mais sera interrompu lors du prochain redémarrage de la JVM.

  • Si l’espace de noms rechargé contient defmulti , vous devez également recharger toutes les expressions defmethod associées.

  • Si l’espace de noms rechargé contient defprotocol , vous devez également recharger tous les enregistrements ou types implémentant ce protocole et remplacer les instances existantes de ces enregistrements / types par de nouvelles instances.

  • Si l’espace de noms rechargé contient des macros, vous devez également recharger les espaces de noms utilisant ces macros.

  • Si le programme en cours d’exécution contient des fonctions fermant les valeurs dans l’espace de noms rechargé, ces valeurs fermées ne sont pas mises à jour. (Ceci est courant dans les applications Web qui construisent la “stack de gestionnaires” en tant que composition de fonctions.)

La bibliothèque clojure.tools.namespace améliore considérablement la situation. Il fournit une fonction d’actualisation simple qui effectue un rechargement intelligent basé sur un graphique de dépendance des espaces de noms.

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

Malheureusement, le rechargement une deuxième fois échouera si l’espace de noms dans lequel vous avez référencé la fonction d’ refresh changé. Cela est dû au fait que tools.namespace détruit la version actuelle de l’espace de noms avant de charger le nouveau code.

 myapp.web=> (refresh) ComstackrException java.lang.RuntimeException: Unable to resolve symbol: refresh in this context, compiling:(/private/var/folders/ks/d6qbfg2s6l1bcg6ws_6bq4600000gn/T/form-init819543191440017519.clj:1:1) 

Vous pouvez utiliser le nom complet de var comme solution de contournement pour ce problème, mais personnellement, je préfère ne pas avoir à le saisir à chaque actualisation. Un autre problème avec ce qui précède est que, après le rechargement de l’espace de noms principal, les fonctions d’assistance REPL standard (telles que doc et source ) ne sont plus référencées ici.

Pour résoudre ces problèmes, je préfère créer un fichier source réel pour l’espace de noms de l’utilisateur afin qu’il puisse être rechargé de manière fiable. Je mets le fichier source dans ~/.lein/src/user.clj mais vous pouvez le placer n’importe où. Le fichier doit avoir besoin de la fonction d’actualisation dans la déclaration top ns comme ceci:

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

Vous pouvez configurer un profil utilisateur leiningen dans ~/.lein/profiles.clj afin que l’emplacement dans lequel vous placez le fichier soit ajouté au chemin de classe. Le profil devrait ressembler à ceci:

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

Notez que je définis l’espace de nom d’utilisateur comme point d’entrée lors du lancement de la REPL. Cela garantit que les fonctions d’assistance REPL sont référencées dans l’espace de noms de l’utilisateur au lieu de l’espace de noms principal de votre application. De cette façon, ils ne seront pas perdus à moins de modifier le fichier source que nous venons de créer.

J’espère que cela t’aides!

La meilleure réponse est:

 (require 'my.namespace :reload-all) 

Cela non seulement rechargera votre espace de noms spécifié, mais rechargera également tous les espaces de noms de dépendances.

Je l’utilise dans Lighttable (et l’instarepl génial) mais il devrait être utile dans d’autres outils de développement. J’avais le même problème avec les anciennes définitions des fonctions et les méthodes multiples qui circulaient après le rechargement, maintenant, au cours du développement, au lieu de déclarer les espaces de noms avec:

 (ns my.namespace) 

Je déclare mes espaces de noms comme ceci:

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

Assez moche mais chaque fois que je réévalue tout l’espace de nommage (Cmd-Shift-Enter dans Lighttable pour obtenir les nouveaux résultats instarepl de chaque expression), il supprime toutes les anciennes définitions et me donne un environnement propre. Je me suis fait prendre tous les deux ou trois jours par d’anciennes définitions avant de commencer à le faire et cela m’a sauvé la santé mentale. 🙂

Essayez à nouveau le fichier de chargement?

Si vous utilisez un IDE, il existe généralement un raccourci clavier pour envoyer un bloc de code à la REPL, redéfinissant ainsi efficacement les fonctions associées.

Un paquebot basé sur la réponse de papachan:

 (clojure.tools.namespace.repl/refresh) 

Dès que (use 'foo.bar) fonctionne pour vous, cela signifie que vous avez foo / bar.clj ou foo / bar_init.class sur votre CLASSPATH. Le bar_init.class serait une version de bar.clj compilée par AOT. Si vous le faites (use 'foo.bar) , je ne suis pas tout à fait sûr que Clojure préfère la classe plutôt que clj ou l’inverse. Si elle préfère les fichiers de classe et que vous avez les deux fichiers, il est clair que la modification du fichier clj, puis le rechargement de l’espace de noms n’ont aucun effet.

BTW: Vous n’avez pas besoin de load-file avant l’ use si votre CLASSPATH est défini correctement.

BTW2: Si vous avez besoin d’utiliser load-file pour une raison, vous pouvez simplement le refaire si vous avez édité le fichier.