2015-08-29 21 views
6

Próbuję zrozumieć zachowanie "Lieningen" podczas tworzenia uberjar. Poniżej znajduje się minimalny przykład, który powiela zachowanie:Dlaczego `lein uberjar` ocenia zmienne zdefiniowane przez` def`?

(ns my-stuff.core 
    (:gen-class)) 

(def some-var (throw (Exception. "boom!"))) 

(defn -main [& args] 
    (println some-var)) 

Gdy ta jest wykonywana z lein run wyraźnie się niepowodzeniem z wyjątek. Jednak nie rozumiem, dlaczego wykonywanie lein uberjar kończy się niepowodzeniem z wyjątku od definicji zmiennej? Dlaczego wykonanie lein uberjar próbuje ocenić wartość zmiennej? Czy jest to zadanie specyficzne dla zadania uberjar, czy też brakuje mi czegoś bardziej merytorycznego na temat Clojure lub Leiningen?

Odpowiedz

10

Aby skompilować twój obszar nazw dla uberjara (jeśli masz włączone AOT), kompilator clojure musi załadować twój obszar nazw. To zawsze wywoła wszystkie efekty uboczne najwyższego poziomu.

Najlepszym sposobem radzenia sobie z tym problemem jest brak efektów ubocznych w kodzie najwyższego poziomu (wewnątrz lub na zewnątrz formularza def) i funkcje inicjujące w celu uzyskania niezbędnych efektów ubocznych startu.

Rozwiązaniem może być, aby zrobić mały nazw, który używa introspekcji załadować resztę swojego kodu w czasie wykonywania, ale nie podczas kompilacji - przy użyciu funkcji takich jak to:

(defn -main 
    [] 
    (require 'my.primary.ns) 
    ((resolve 'my.primary.ns/start))) 

jeśli nazw jest sporządzony, jvm może znaleźć -main i uruchomić go, mimo że żaden inny twój kod nie jest skompilowany. Środowisko wykonawcze require spowoduje, że kompilator Clojure załaduje resztę kodu tylko w środowisku wykonawczym, a resolve jest potrzebny, aby -main kompilował się w sposób czysty - zwraca zmienną, do której odwołuje się funkcja, która wywołuje twoją funkcję po wywołaniu.