2017-05-04 35 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