191 votes

Comment recharger un fichier clojure dans REPL

Quelle est la meilleure façon de recharger les fonctions définies dans un fichier Clojure sans avoir à redémarrer le REPL ? Actuellement, pour pouvoir 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 donne pas l'effet requis, qui est d'évaluer les corps modifiés des fonctions et de retourner de nouvelles valeurs, au lieu de se comporter comme si la source n'avait pas changé du tout.

Documentation :

21 votes

(use 'foo.bar :reload-all) a toujours bien fonctionné pour moi. Aussi, (load-file) ne devrait jamais être nécessaire si votre classpath est bien configuré. Quel est "l'effet requis" que vous n'obtenez pas ?

0 votes

Oui, qu'est-ce que "l'effet requis" ? Affichez un échantillon bar.clj en détaillant "l'effet recherché".

1 votes

Par effet requis, je voulais dire que si j'avais une fonction (defn f [] 1) et j'ai changé sa définition en (defn f [] 2) il m'a semblé qu'après avoir émis (use 'foo.bar :reload-all) et appeler le f il devrait retourner 2, et non 1. Malheureusement, cela ne fonctionne pas de cette façon pour moi et chaque fois que je change le corps de la fonction, je dois redémarrer le REPL.

224voto

Ming Points 543

Ou (use 'your.namespace :reload)

4 votes

:reload-all devrait également fonctionner. L'OP dit spécifiquement que ce n'est pas le cas, mais je pense qu'il y a quelque chose d'autre qui ne va pas dans l'environnement de développement de l'OP parce que pour un seul fichier les deux ( :reload y :reload-all ) devrait avoir le même effet. Voici la commande complète pour :reload-all : (use 'your.namespace :reload-all) Cela recharge toutes les dépendances, aussi.

82voto

papachan Points 900

Il existe également une alternative comme l'utilisation de outils.espace-nom c'est assez efficace :

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

user=> (refresh)

:reloading (namespace.app)

:ok

3 votes

Cette réponse est plus appropriée

12 votes

Mise en garde : la course à pied (refresh) semble également faire en sorte que le REPL oublie que vous avez exigé la présence de clojure.tools.namespace.repl . Les appels ultérieurs à (refresh) vous donnera une RuntimeException, "Impossible de résoudre le symbole : refresh dans ce contexte". La meilleure chose à faire est sans doute de (require 'your.namespace :reload-all) ou, si vous savez que vous allez vouloir rafraîchir votre REPL souvent pour un projet donné, faire un :dev et ajouter [clojure.tools.namespace.repl :refer (refresh refresh-all)] a dev/user.clj .

1 votes

Blogpost sur le workflow de Clojure par l'auteur de tools.namespace : thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded

72voto

Dirk Geurs Points 591

Rechargement du code Clojure à l'aide de (require … :reload) y :reload-all es très problématique :

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

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

  • Si l'espace de noms rechargé contient defmulti vous devez également recharger tous les éléments associés defmethod expressions.

  • Si l'espace de noms rechargé contient defprotocol vous devez également recharger tout enregistrement ou type implémentant ce protocole et remplacer toutes les instances existantes de ces enregistrements/types par de nouvelles instances.

  • Si l'espace de noms rechargé contient des macros, vous devez également recharger tout espaces de noms qui utilisent ces macros.

  • Si le programme en cours d'exécution contient des fonctions qui ferment sur des valeurs dans le fichier l'espace de nom rechargé, ces valeurs fermées ne sont pas mises à jour. (Ceci est courant dans les applications Web qui construisent la "pile de gestionnaires stack" comme une composition de fonctions).

La bibliothèque clojure.tools.namespace améliore la situation de manière significative. Elle fournit une fonction de rafraîchissement facile qui effectue un rechargement intelligent basé sur un graphe de dépendances 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 de nom dans lequel vous avez référencé le fichier refresh fonction modifiée. 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 pourriez utiliser le nom de var pleinement qualifié comme solution de rechange à ce problème, mais personnellement, je préfère ne pas avoir à le saisir à chaque rafraîchissement. 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 du REPL (telles que doc y source ) n'y sont plus référencés.

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 place le fichier source dans ~/.lein/src/user.clj mais vous pouvez le placer n'importe où. Le fichier doit exiger la fonction de rafraîchissement dans la déclaration de la ns supérieure, comme ceci :

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

Vous pouvez configurer un profil d'utilisateur de leiningen sur ~/.lein/profiles.clj afin que l'emplacement dans lequel vous placez le fichier soit ajouté au chemin de la classe. Le profil doit ressembler à quelque chose comme 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 du REPL. Cela garantit que les fonctions d'aide du REPL seront 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, elles ne seront pas perdues si vous ne modifiez pas le fichier source que nous venons de créer.

J'espère que cela vous aidera !

0 votes

De bonnes suggestions. Une question : pourquoi l'entrée ":source-paths" ci-dessus ?

0 votes

OK, j'ai trouvé la réponse. Le fichier "user.clj" doit vivre quelque part, et un bon endroit est "/home/alan/.lein/user.clj" (sous linux). Pour que lein trouve le fichier, nous avons besoin d'un fichier "/home/alan/.lein/profiles.clj " avec une entrée comme : ':source-paths [ "/home/alan/.lein" '

0 votes

Exactement, avoir un fichier source réel permet de recharger de manière fiable ce fichier et l'espace de nom qu'il contient. Voici le fichier que j'utilise actuellement : github.com/Dirklectisch/.lein/blob/master/src/user.clj

53voto

Alan Thompson Points 325

La meilleure réponse est :

(require 'my.namespace :reload-all)

Cela ne rechargera pas seulement l'espace de noms spécifié, mais aussi tous les espaces de noms dépendants.

Documentation :

exiger

3 votes

C'est la seule réponse qui a fonctionné avec lein repl Coljure 1.7.0 et nREPL 0.3.5. Si vous êtes nouveau à Clojure : L'espace de nom ( 'my.namespace ) est défini avec (ns ...) sur src/ ... /core.clj par exemple.

1 votes

Le problème avec cette réponse est que la question originale utilise (load-file ...), pas de require. Comment peut-elle ajouter le :reload-all à l'espace de noms après le load-file ?

0 votes

Parce que la structure de l'espace de noms comme proj.stuff.core reflète la structure du fichier sur le disque comme src/proj/stuff/core.clj le REPL peut localiser le bon fichier et il n'est pas nécessaire d'utiliser la fonction load-file .

3voto

Paul Lam Points 649

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 au REPL, ce qui permet de redéfinir les fonctions associées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X