2017-05-04 46 views
1

我做了同樣愚蠢的錯誤很多很多次:命名空間限定的記錄字段訪問器

(defrecord Record [field-name]) 

(let [field (:feld-name (->Record 1))] ; Whoops! 
    (+ 1 field)) 

因爲我拼錯了字段名稱關鍵字,這將導致NPE。

的「明顯」的解決方案,這將有defrecord發出的命名空間的關鍵字,而不是,從那時起,特別是在不同的文件中工作時,該IDE就能立即顯示出哪些關鍵字,一旦可作爲I型::n/

我大概可以用一些創造力創建一個包裝defrecord的宏,它爲我創建了關鍵字,但這看起來像是矯枉過正。

有沒有辦法讓defrecord發出命名空間字段訪問器,或者有沒有其他好辦法來避免這個問題?

回答

3

由於defrecords編譯爲java類和java類的字段沒有名稱空間的概念,我不認爲有defrecord發射命名空間關鍵字的好方法。

一種替代方案是,如果代碼不是性能敏感的,並且不需要實現任何協議和類似的功能,則只需使用地圖。

另一個就像Alan Thompson的解決方案一樣,可以實現安全功能。 prismatic/plumbing util庫也有這個實現。

(defn safe-get [m k] 
    (let [ret (get m k ::not-found)] 
    (if (= ::not-found ret) 
     (throw (ex-info "Key not found: " {:map m, :key k})) 
     ret))) 

(defrecord x [foo]) 

(safe-get (->x 1) :foo) ;=> 1 

(safe-get (->x 1) :fo) ;=> 

;; 1. Unhandled clojure.lang.ExceptionInfo 
;; Key not found: 
;; {:map {:foo 1}, :key :fo} 
1

我感到你的痛苦。幸運的是,我有一個解決方案,可以節省我多次,每週使用幾年。這是the grab function from the Tupelo library。它不提供您希望的IDE集成類型,但它確實提供故障快速排字錯誤檢測,所以您總是被通知第一次您嘗試使用不存在的鍵。另一個好處是,你會得到一個堆棧跟蹤,其中顯示的是帶拼寫錯誤的關鍵字的行號,而非nil值導致NPE的行號(可能很遠,很遠)。

它也適用於兩個記錄&普通舊地圖(我通常的用例)。

自述:


地圖值查找

地圖是方便的,尤其是當關鍵字用作功能來查找值的映射。不幸的是,試圖在地圖中查找不存在的關鍵字將返回零。雖然有時很方便,但這意味着關鍵字名稱中的簡單拼寫錯誤會默默返回損壞的數據(即nil)而不是所需的值。

相反,使用關鍵字/地圖查找功能抓鬥:

(grab k m) 
    "A fail-fast version of keyword/map lookup. When invoked as (grab :the-key the-map), 
    returns the value associated with :the-key as for (clojure.core/get the-map :the-key). 
    Throws an Exception if :the-key is not present in the-map." 

(def sidekicks {:batman "robin" :clark "lois"}) 
(grab :batman sidekicks) 
;=> "robin" 

(grab :spiderman m) 
;=> IllegalArgumentException Key not present in map: 
map : {:batman "robin", :clark "lois"} 
keys: [:spiderman] 

功能搶也應該到位clojure.core的使用/獲取。簡單地顛倒參數的順序以匹配「keyword-first,map-second」約定。

對於嵌套地圖查找值,該函數取入中替換clojure.core /獲取時間:

(fetch-in m ks) 
    "A fail-fast version of clojure.core/get-in. When invoked as (fetch-in the-map keys-vec), 
    returns the value associated with keys-vec as for (clojure.core/get-in the-map keys-vec). 
    Throws an Exception if the path keys-vec is not present in the-map." 

(def my-map {:a 1 
      :b {:c 3}}) 
(fetch-in my-map [:b :c]) 
3 
(fetch-in my-map [:b :z]) 
;=> IllegalArgumentException Key seq not present in map: 
;=> map : {:b {:c 3}, :a 1} 
;=> keys: [:b :z] 

您的其他選項,使用記錄,是使用Java的互操作訪問的風格:

(.field-name myrec) 

由於Clojure的defrecord編譯成一個簡單的Java類,你的IDE 可能能夠識別出這些名字更EA甲硅烷。YMMV

+0

謝謝。我打算讓這個開放一點,因爲我希望有一個不需要包含第三方的解決方案。 – Carcigenicate