Fractionnement d'un espace de noms Clojure sur plusieurs fichiers

91

Est-il possible de diviser un espace de noms Clojure sur plusieurs fichiers source lors de la compilation à l'avance avec :gen-class? Comment faire (:main true)et (defn- ...)entrer en jeu?

Ralph
la source

Réponses:

137

Aperçu

Vous pouvez certainement, en fait, l' clojure.coreespace de noms lui-même est divisé de cette manière et fournit un bon modèle que vous pouvez suivre en regardant dans src/clj/clojure:

core.clj
core_deftype.clj
core_print.clj
core_proxy.clj
..etc..

Tous ces fichiers participent à la construction du single clojure.core espace de noms .

Fichier principal

L'un de ces fichiers est le fichier principal, nommé pour correspondre au nom de l'espace de noms afin qu'il soit trouvé lorsque quelqu'un le mentionne dans un fichier :useou :require. Dans ce cas, le fichier principal est clojure/core.clj, et il commence par un nsformulaire. C'est là que vous devez placer toute la configuration de votre espace de noms, quels que soient vos autres fichiers qui en ont besoin. Cela inclut normalement :gen-classaussi, donc quelque chose comme:

(ns my.lib.of.excellence
  (:use [clojure.java.io :as io :only [reader]])
  (:gen-class :main true))

Ensuite, aux endroits appropriés de votre fichier principal (le plus souvent à la fin), utilisez loadpour importer vos fichiers d'aide. En clojure.corecela ressemble à ceci:

(load "core_proxy")
(load "core_print")
(load "genclass")
(load "core_deftype")
(load "core/protocols")
(load "gvec")

Notez que vous n'avez pas besoin du répertoire courant comme préfixe, ni du .cljsuffixe.

Fichiers d'aide

Chacun des fichiers d'assistance doit commencer par déclarer l'espace de noms qu'il aide, mais doit le faire en utilisant la in-nsfonction. Ainsi, pour l'exemple d'espace de noms ci-dessus, les fichiers d'assistance commenceraient tous par:

(in-ns 'my.lib.of.excellence)

C'est tout ce qu'il faut.

classe gen

Étant donné que tous ces fichiers créent un espace de noms unique, chaque fonction que vous définissez peut se trouver dans l'un des fichiers principaux ou auxiliaires. Cela signifie bien sûr que vous pouvez définir vos gen-classfonctions dans n'importe quel fichier de votre choix:

(defn -main [& args]
  ...)

Notez que les règles normales d'ordre de définition de Clojure s'appliquent toujours à toutes les fonctions, vous devez donc vous assurer que tout fichier définissant une fonction est chargé avant d'essayer d' utiliser cette fonction.

Vars privé

Vous avez également posé des questions sur le (defn- foo ...)formulaire qui définit une fonction d'espace de noms privé. Les fonctions définies comme celle-ci ainsi que d'autres :privatevariables sont visibles depuis l'espace de noms dans lequel elles sont définies, de sorte que les fichiers principaux et tous les fichiers auxiliaires auront accès aux variables privées définies dans l'un des fichiers chargés jusqu'à présent.

Chouser
la source
3
Très belle réponse complète! BTW, j'ai presque terminé mon premier passage à travers The Joy of Clojure . Super livre!
Ralph
Merci d'avoir partagé cette réponse. Est-ce encore considéré comme une bonne pratique, 2 ans plus tard? (Je sais que les choses changent rapidement.) Je vois que Clojure lui-même utilise toujours cette technique.
David J.
9
À ce jour, cette pratique est toujours la meilleure si vous êtes sûr de vouloir que plusieurs fichiers génèrent un seul espace de noms. Cependant, cela lui-même est peut-être moins courant aujourd'hui qu'il ne l'était. Une alternative peut être de définir toutes les variables publiques de vos ns dans un seul fichier, et de déplacer toutes les variables et fonctions d'assistance vers un espace de noms «d'implémentation» séparé. Les vars dans impl seraient techniquement publics, mais une docstring ns indiquant qu'ils ne font pas partie de l'API documentée est courante et devrait être suffisante.
Chouser
1
Savons-nous si des outils Clojure courants ont des problèmes pour comprendre les espaces de noms multi-fichiers? Lein? Démarrage? Cidre? nREPL? Kibit? Eastwood? Cloverage? Etc ...
Didier A.