Suddivisione di uno spazio dei nomi Clojure su più file


91

È possibile dividere uno spazio dei nomi Clojure su più file di origine quando si esegue la compilazione anticipata con :gen-class? Come fare (:main true)e (defn- ...)entrare in gioco?

Risposte:


137

Panoramica

Certamente puoi, infatti lo clojure.corestesso spazio dei nomi è suddiviso in questo modo e fornisce un buon modello che puoi seguire guardando in src/clj/clojure:

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

Tutti questi file partecipano alla costruzione del singolo clojure.core spazio dei nomi.

File primario

Uno di questi è il file principale, denominato in modo che corrisponda al nome dello spazio dei nomi in modo che venga trovato quando qualcuno lo menziona in :useo :require. In questo caso il file principale è clojure/core.clj, e inizia con un nsmodulo. Qui è dove dovresti mettere tutta la configurazione dello spazio dei nomi, indipendentemente da quale degli altri tuoi file potrebbe averne bisogno. Questo normalmente include :gen-classanche, quindi qualcosa come:

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

Quindi nei punti appropriati nel tuo file principale (più comunemente tutti alla fine) usa loadper portare i tuoi file di supporto. In clojure.coresembra questo:

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

Nota che non hai bisogno della directory corrente come prefisso, né hai bisogno del .cljsuffisso.

File di supporto

Ciascuno dei file di supporto dovrebbe iniziare dichiarando quale spazio dei nomi sta aiutando, ma dovrebbe farlo usando la in-nsfunzione. Quindi, per lo spazio dei nomi di esempio sopra, i file helper inizierebbero tutti con:

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

È tutto ciò che serve.

gen-class

Poiché tutti questi file creano un unico spazio dei nomi, ogni funzione definita può trovarsi in uno qualsiasi dei file principali o di supporto. Questo ovviamente significa che puoi definire le tue gen-classfunzioni in qualsiasi file desideri:

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

Nota che le normali regole dell'ordine di definizione di Clojure si applicano ancora a tutte le funzioni, quindi devi assicurarti che qualunque file definisce una funzione sia caricato prima di provare a usare quella funzione.

Vars privato

Hai anche chiesto informazioni sul (defn- foo ...)modulo che definisce una funzione privata dello spazio dei nomi. Le funzioni definite in questo modo e altre :privatevariabili sono visibili dall'interno dello spazio dei nomi in cui sono definite, quindi i file primari e tutti gli helper avranno accesso alle variabili private definite in uno qualsiasi dei file caricati finora.


3
Risposta molto bella e completa! A proposito, ho quasi finito il mio primo passaggio attraverso The Joy of Clojure . Ottimo libro!
Ralph

Grazie per aver condiviso questa risposta. È considerata ancora una buona pratica, 2 anni dopo? (So ​​che le cose cambiano velocemente.) Vedo che Clojure stesso usa ancora questa tecnica.
David J.

9
Ad oggi questa è ancora la migliore pratica se sei sicuro di volere più file per generare un unico spazio dei nomi. Tuttavia, questo potrebbe essere meno comune ora di quanto non fosse. Un'alternativa potrebbe essere quella di definire tutte le variabili pubbliche del tuo ns in un singolo file e spostare tutte le variabili e le funzioni di supporto in uno spazio dei nomi di "implementazione" separato. Le variabili in impl sarebbero tecnicamente pubbliche, ma una docstring ns che indica che non fanno parte dell'API documentata è comune e dovrebbe essere sufficiente.
Chouser

1
Sappiamo se qualche strumento comune di Clojure ha problemi a comprendere gli spazi dei nomi multi-file? Lein? Stivale? Cedro? nREPL? Kibit? Eastwood? Cloverage? Etc ...
Didier A.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.