web-dev-qa-db-fra.com

Comment recharger un fichier de clojure dans REPL

Quelle est la méthode préférée pour recharger les fonctions définies dans un fichier Clojure sans avoir à redémarrer le REPL. À l'heure actuelle, pour utiliser le fichier mis à jour, je dois:

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

En outre, (use 'foo.bar :reload-all) ne produit pas l'effet requis, qui consiste à évaluer les corps de fonctions modifiés et à renvoyer de nouvelles valeurs, au lieu de se comporter comme la source n'a pas changé du tout.

Documentation:

156
pkaleta

Ou (use 'your.namespace :reload)

183
Ming

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
72
papachan

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

  • Si vous modifiez deux espaces de noms qui dépendent l'un de l'autre, n'oubliez pas de les recharger dans le bon ordre pour éviter les erreurs de compilation.

  • Si vous supprimez les définitions d'un fichier source, puis 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 se cassera au prochain redémarrage de la machine virtuelle.

  • 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 mettant en œuvre ce protocole et remplacer toutes les instances existantes de ces enregistrements/types par de nouvelles instances.

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

  • Si le programme en cours d'exécution contient des fonctions qui recouvrent des valeurs dans l'espace de noms rechargé, ces valeurs ne sont pas mises à jour. (Ceci est courant dans les applications Web qui construisent la "pile 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 facile 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 seconde fois échouera si l’espace-noms dans lequel vous avez référencé la fonction refresh a 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)

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)

Vous pouvez utiliser le nom de variable complet comme solution de contournement pour résoudre 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 qu’après le rechargement de l’espace de noms principal, les fonctions d’aide standard REPL (comme doc et source ne sont plus utilisées.)).

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, de manière à ce qu'il puisse être rechargé de manière fiable. Je mets le fichier source dans ~/.lein/src/user.clj mais vous pouvez placer n'importe où. Le fichier devrait nécessiter la fonction d'actualisation dans la déclaration ns supérieure, comme ceci:

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

Vous pouvez configurer n profil utilisateur leiningen dans ~/.lein/profiles.clj afin que l'emplacement dans lequel vous avez inséré 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 j'ai défini l'espace de nom de l'utilisateur comme point d'entrée lors du lancement de la réplique. Cela garantit que les fonctions d’aide REPL) sont référencées dans l’espace de nom d’utilisateur plutôt que dans l’espace de nom principal de votre application. Ainsi, elles ne seront perdues que si vous modifiez le fichier source que nous venons de créer.

J'espère que cela t'aides!

55
Dirk Geurs

La meilleure réponse est:

(require 'my.namespace :reload-all)

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

Documentation:

nécessite

36
Alan Thompson

J'utilise ceci dans Lighttable (et dans l'installateur 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 de fonctions et les méthodes multiples qui traînaient après les rechargements, maintenant au cours du développement, au lieu de déclarer des 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 l’espace de noms entier (Cmd-Shift-Enter dans Lighttable pour obtenir les nouveaux résultats instarepl de chaque expression), il efface toutes les anciennes définitions et me fournit un environnement propre. Avant que je ne commence à faire cela, je me faisais trébucher tous les jours ou quelques jours par les anciennes définitions, ce qui m'a sauvé la santé mentale. :)

4
optevo

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

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

Essayez de charger le fichier à nouveau?

Si vous utilisez un IDE, il existe généralement un raccourci clavier pour envoyer un bloc de code à l'EPL, ce qui permet de redéfinir les fonctions associées.

2
Paul Lam

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. Bar_init.class serait une version compilée par AOT de bar.clj. Si tu fais (use 'foo.bar) _, Je ne sais pas si Clojure préfère les cours à clj ou inversement. S'il 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 le use si votre CLASSPATH est défini correctement.

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

0
Tassilo Horn