2014-10-27 45 views
1

根據規範,def應該在當前ns中實現var(即* ns *)。但是,下面的代碼看起來不像這樣:Clojure使用什麼命名空間來定義

(ns namespace-b) 

(defn def_something [] 
    (ns namespace-a) 
    (println *ns*) ;prints namespace-a as it should 
    (def something 1) 
) 

(def_something) 

(println namespace-b/something) ; prints 1 
(println namespace-a/something) ; throws 

我錯過了什麼?

注:

  • defn是隻用於清晰度。定義和運行匿名函數也同樣適用。
  • 我知道使用def裏面的函數可能不是很習慣。然而,這只是我遇到的一個更大問題的精髓。
+0

不僅是函數裏面的'def'不是慣用的,它不會做你期望的(正如你正在學習的那樣)。最好直接使用'intern',或者至少使用一個宏,以便def在適當的命名空間中完成它的工作。 – noisesmith 2014-10-27 16:43:41

回答

3

解析器已經實習生在編譯時的VAR到當前的命名空間,雖然不會立即被束縛:

(defn dd [] (def x 0)) 
x ;; => #<Unbound Unbound: #'user/x> 

相關的代碼可以發現here,與第二個參數爲lookupVar,觸發前述的不存在的變量here的實習。

解析then generates an expression that references the previously created var,所以表達式邏輯永遠不會離開當前的命名空間。

TL; DR:def是編譯器以特殊方式處理的東西。

+0

順便說一句,這是多麼混亂的編譯器代碼... – xsc 2014-10-27 14:09:05

+0

我明白,但這是否意味着應該糾正規範?我沒有在任何地方看到'def'的正確描述。 – 2014-10-27 17:01:28

+1

另一個相關的問題:你從頭開始Leinigen項目。定義函數'myfunc'並從'-main'中調用它。這樣做雖然可行,但它不應該注意到,Leiningen在新創建的「用戶」名稱空間中運行了-main,它不包含「myfunc」既不作爲別名也不作爲引用。這只是一個奇怪的'def'行爲的後果,這種代碼正在工作。而且,如果你在-main中運行(eval'(myfunc)),它會崩潰(因爲它應該是IMO)。現在如果你直接運行代碼(沒有Leiningen),一切正常。我明白這裏發生了什麼,但是男人,這很醜陋。 – 2014-10-27 17:13:34

1

瞭解def的關鍵是它是一個宏。這意味着它不會在運行時解析命名空間或創建綁定,但是在編譯代碼的時候事先要做。

如果調用一個調用def的函數,那麼對def的調用已解析爲使用定義函數的名稱空間。同樣,如果您在函數體內調用函數,則要在調用該函數的名稱空間內的編譯時解析要調用的函數。

如果您希望在運行時將值綁定到名稱空間,則應該使用函數intern,該函數允許您明確地將名稱空間設置爲mutate。所有這一切都說,命名空間變異就是這樣,它是過程式的,並且不是線程安全的,並且不像Clojure提供的其他選項那樣具有很好的聲明性語義。我強烈建議找到一種方法來表達不涉及不安全運行時突變的解決方案。