9 votes

Comment organisez-vous les noms de fonctions lorsque vous construisez des bibliothèques clojure pour le grand public ?

Supposons que je veuille construire une grande bibliothèque clojure avec plusieurs composants. En tant que développeur, j'aimerais conserver plusieurs de ces composants dans des espaces de noms séparés, car de nombreuses fonctions d'aide peuvent avoir des noms similaires. Je ne veux pas nécessairement rendre les choses privées car elles peuvent avoir une utilité en dehors des cas extrêmes et les solutions de contournement derrière private ne sont pas bonnes. (En d'autres termes, j'aimerais suggérer l'utilisation du code, et non l'empêcher complètement).

Cependant, j'aimerais que les utilisateurs de la bibliothèque opèrent dans un espace de noms avec une union d'un sous-ensemble de nombreuses fonctions dans chaque sous-bibliothèque. Quelle est la meilleure façon idiomatique de procéder ? Une solution qui me vient à l'esprit est d'écrire une macro qui génère :requires et crée un nouveau mapping de var en définissant une liste donnée de noms de var (voir le premier exemple de code). Y a-t-il des compromis avec cette méthode, comme par exemple ce qui se passe avec l'extension des types ? Existe-t-il une meilleure méthode (ou une meilleure intégration) ?

Exemple de macro (src/mylib/public.clj) :

 (ns mylib.public
    (:require [mylib.a :as a])
    (:require [mylib.b :as b]))

 (transfer-to-ns [+ a/+
                  - b/-
                  cat b/cat
                  mapper a/mapper])

Encore une fois, pour clarifier, le but final serait d'avoir un fichier dans d'autres projets créés par les utilisateurs de mylib pour pouvoir faire quelque chose comme (src/someproject/core.clj) :

 (ns someproject.core
     (:require [mylib.public :as mylib]))

 (mylib/mapper 'foo 'bar)

@Jeremy Wall, notez que la solution que vous proposez ne répond pas à mes besoins. Supposons que le code suivant existe.

mylib/a.clj :

 (ns mylib.a)

 (defn fa [] :a)

mylib/b.clj :

 (ns mylib.b)

 (defn fb [] :b)

mylib/public.clj :

 (ns mylib.public
     (:use [mylib.a :only [fa]])
     (:use [mylib.b :only [fb]]))

somerandomproject/core.clj : (Suppose que les chemins d'accès sont correctement définis)

 (ns somerandomproject.core
     (:require [mylib.public :as p])

 ;; somerandomproject.core=> (p/fa)
 ;; CompilerException java.lang.RuntimeException: No such var: p/fa, compiling:     (NO_SOURCE_PATH:3) 
 ;; somerandomproject.core=> (mylib.a/fa)
 ;; :a

Si vous remarquez, les fonctions "using" dans mylib/public.clj NE PERMETTENT PAS à public.clj de FOURNIR ces variables au fichier utilisateur somerandomproject/core.clj.

9voto

Alex Miller Points 28225

Il peut être intéressant de voir comment une bibliothèque comme Compojure o Lamina organise son api "publique".

Lamina dispose d'espaces de noms "publics" tels que lamina.api qui servent à aliaser (en utilisant l'import-fn de Zach de sa bibliothèque potemkin) les fonctions des espaces de noms "internes" comme lamina.core.pipeline . Avec un peu de documentation, cela permet de délimiter clairement les NS orientés vers le public des internes susceptibles d'être modifiés. J'ai trouvé que le principal inconvénient de cette stratégie est que l'import-fn rend beaucoup plus difficile le passage (dans emacs) de l'utilisation d'une fonction à son implémentation. Ou de savoir sur quelle fonction utiliser clojure.repl/source par exemple.

Une bibliothèque comme Compojure utilise des variables privées pour séparer les parties publiques/privées des fonctions (voir compojure.core par exemple). L'inconvénient majeur des fonctions "privées" est que l'on peut se rendre compte plus tard que l'on veut les exposer ou que cela rend les tests plus compliqués. Si vous contrôlez la base de code, je ne pense pas que les aspects privés soient un gros problème. Le problème des tests est facilement résolu en utilisant #'foo.core/my-function pour faire référence à la fonction privée.

En général, j'ai tendance à utiliser quelque chose qui ressemble plus au style de Compojure qu'à celui de Lamina, mais il semble que vous préfériez quelque chose qui ressemble plus à Lamina.

2voto

Jeremy Wall Points 10643

Je ne sais pas exactement ce que vous demandez ici. Je pense que vous voulez savoir quelle est la meilleure pratique pour importer des publics à partir d'un espace de noms pour des fonctions utilitaires partagées ? Dans ce cas, la fonction refer est ce que vous recherchez, je pense : http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/refer

(refer mylib.a :only [+])
(refer mylib.b :only [-])

Il importe les éléments publics d'un espace de noms dans l'espace de noms actuel. Toutefois, la méthode préférée consiste à le faire dans la déclaration de l'espace de noms à l'aide de la directive :use

(ns (:use (mylib.a :only [+])
          (mylib.b :only [-])))

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