2012-12-06 13 views
5

Często chcę uruchomić mały fragment kodu w innym obszarze nazw - na przykład kopię/wklejenie fragmentu kodu DSL i chciałbym uniknąć konieczności:Uruchamiaj kod osadzony z innej przestrzeni nazw

  • Dodaj kilka klauzul use do mojej obecnej deklaracji przestrzeni nazw. To sprawia, że ​​deklaracja ns jest chaotyczna, dodaje dodatkowe prace konserwacyjne, a czasem grozi konfliktami nazw.
  • Dodaj klauzule require i zostań zmuszony do dodania do wszystkich kwalifikatora lub aliasu przestrzeni nazw. Teraz mój kod DSL jest znacznie bardziej nieuporządkowany.

Idealnie wolałbym być w stanie zrobić coś takiego:

(with-ns my.namespace 
    (foo bar baz)) 

Gdzie foo, bar może być symbole wewnątrz my.namespace, ale baz jest symbol w bieżącym (zamykającego) nazw. Tak więc kod działa w czymś podobnym do "lokalnej" przestrzeni nazw, która "wykorzystuje" moją-przestrzeń nazw w swoim zasięgu, ale w inny sposób nie wpływa na otaczającą przestrzeń nazw.

Czy istnieje standardowy/lepszy sposób na zrobienie tego? A może chcesz to zrobić?

+0

używam obciążenia plik do rodzaju dostać to, to hack: -/Więc nie jesteś jedyny, wciąż może być szalony. –

+0

@Arthur - hmmm zobacz, jak to może działać, ale często fragment nie znajduje się w osobnym pliku, również wydaje się nadużyciem systemu plików, kiedy tego nie potrzebujemy. Dobrze cię poznać w Conj BTW! – mikera

+0

Ogranicza się, ponieważ używa DSL (w moim przypadku definicje sieci + vm muszą być jedno na plik, więc nie jest to dobra odpowiedź, fajnie było Cię poznać :) –

Odpowiedz

4

Spróbuj tego:

(defmacro with-ns [[namespace symbols] & body] 
    `(do (use '[~namespace :only ~symbols]) 
     (let [result# (do [email protected])] 
     (doseq [sym# (map #(:name (meta (val %))) 
          (filter #(= (name '~namespace) 
             (str (:ns (meta (val %))))) 
            (ns-refers *ns*)))] 
      (ns-unmap *ns* sym#)) 
     result#))) 

(with-ns [clojure.string [split upper-case]] 
    (split (upper-case "it works!") #" ")) 
-> ["IT" "WORKS!"] 

Po pracy usuwa używane symbole z aktualnymi ns.

3

Można to osiągnąć za pomocą makra, jak pokazano poniżej.

UWAGA: To może złamać w niektórych przypadkach po prostu próbowałem go z prostym przykładzie

;Some other ns 
(ns hello) 
(def h1 10) ;in hello 
(def h2 11) ;in hello 

;main ns in which executing code 
(ns user) 


(defmacro with-ns [target-ns body] 
    (clojure.walk/postwalk 
    (fn [val] 
    (if (symbol? val) 
     (if (resolve (symbol (str target-ns "/" val))) 
     (symbol (str target-ns "/" val)) 
     val) val)) body)) 

(def u1 100) ;in user 

(with-ns hello (do (+ h1 u1))) ;110 
+0

Myślę, że dużą wadą tego rozwiązania (w najmniej w obecnej formie) jest to, że zastępuje symbole związane w dowolnych zagnieżdżonych blokach "niech". Ale wciąż +1 ode mnie. –

0

I w końcu znalazłem makro w starym Clojure contrib, dokłada częścią tego całkiem zgrabnie:

(defmacro with-ns 
    "Evaluates body in another namespace. ns is either a namespace 
    object or a symbol. This makes it possible to define functions in 
    namespaces other than the current one." 
    [ns & body] 
    `(binding [*ns* (the-ns ~ns)] 
    [email protected](map (fn [form] `(eval '~form)) body))) 
+0

To nie jest rozwiązanie, ponieważ nie ma symboli od bieżącego ns ("baz" w twoim przykładzie) w tymczasowej przestrzeni nazw "ns". Lub pytanie dotyczyło tymczasowego przełączania pełnego obszaru nazw bez dostępu do symboli z bieżącego ns? – mobyte

+0

Tak, masz rację - nie rozwiązuje to wszystkich problemów. – mikera

+0

Możesz dostosować makro, aby skierować inną przestrzeń nazw. – Bill

Powiązane problemy