命名空间限定记录字段访问者

时间:2017-05-04 18:01:04

标签: clojure record keyword

我多次犯了同样的愚蠢错误:

(defrecord Record [field-name])

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

由于我拼错了字段名称关键字,这将导致NPE。

对此的“明显”解决方案是让defrecord发出命名空间关键字,从那时起,特别是当在不同的文件中工作时,IDE将能够立即显示哪些关键字可用。我输入::n/

我可能会有一些创造力创建一个包裹defrecord的宏,为我创建关键字,但这看起来有点过分。

有没有办法让defrecord发出命名空间的字段访问器,还是有其他好方法可以避免这个问题?

2 个答案:

答案 0 :(得分:4)

因为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 :(得分:2)

我感觉到你的痛苦。值得庆幸的是,我有一个解决方案可以节省我很多次/周,我已经使用了几年。它是the grab function from the Tupelo library。它不提供您希望的IDE集成类型,但它确实提供快速错字检测,因此始终首先通知时间您尝试使用不存在的密钥。另一个好处是,您将获得一个堆栈跟踪,其中显示带有拼写错误关键字的行号,而不是nil值导致NPE的行号(可能是远,远)。

它对记录和记录也同样有效。普通地图(我通常的用例)。

来自自述文件:

地图值​​查找

地图很方便,尤其是当关键字用作在地图中查找值的函数时。不幸的是,尝试在地图中查找不存在的关键字将返回nil。虽然有时很方便,但这意味着关键字名称中的简单拼写错误将以静默方式返回损坏的数据(即nil)而不是所需的值。

相反,使用函数grab进行关键字/地图查找:

(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]

还应该使用函数grab来代替clojure.core / get。简单地颠倒参数的顺序以匹配"关键字 - 第一,map-second"约定。

为了在嵌套地图中查找值,函数fetch-in取代了clojure.core / get-in:

(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-interop样式的访问者:

(.field-name myrec)

由于Clojure defrecord编译成一个简单的Java类,IDE 可能能够更容易地识别这些名称。 YMMV