Clojure 命名空间限定的记录字段访问器

Clojure 命名空间限定的记录字段访问器,clojure,record,keyword,Clojure,Record,Keyword,我犯过很多次同样愚蠢的错误: (defrecord Record [field-name]) (let [field (:feld-name (->Record 1))] ; Whoops! (+ 1 field)) 由于我拼错了field name关键字,这将导致NPE “显而易见”的解决方案是让defrecord发出名称空间的关键字,从那时起,特别是在不同的文件中工作时,只要我键入::n/,IDE就能立即显示哪些关键字可用 我也许可以创造性地创建一个宏来包装defrecord,

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

(defrecord Record [field-name])

(let [field (:feld-name (->Record 1))] ; Whoops!
  (+ 1 field))
由于我拼错了field name关键字,这将导致NPE

“显而易见”的解决方案是让
defrecord
发出名称空间的关键字,从那时起,特别是在不同的文件中工作时,只要我键入
::n/
,IDE就能立即显示哪些关键字可用

我也许可以创造性地创建一个宏来包装
defrecord
,为我创建关键字,但这似乎有些过分了


有没有办法让
defrecord
发出带名称空间的字段访问器,或者有没有其他好办法来避免这个问题?

我感觉到了你的痛苦。谢天谢地,我有一个解决方案,每周可以节省我几年来使用的次数。它是。它不提供您希望的IDE集成类型,但它确实提供了快速失败的键入检测,因此当您尝试使用不存在的键时,总是会通知您。另一个好处是,您将获得一个stacktrace,其中显示了拼写错误的关键字的行号,而不是
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。只需颠倒参数顺序以匹配“关键字优先,映射第二”约定

为了在嵌套映射中查找值,函数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互操作风格的访问器:

(.field-name myrec)

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

因为defrecords编译成java类,java类上的字段没有名称空间的概念,所以我认为没有一种好方法让defrecord发出名称空间关键字

如果代码对性能不敏感,并且不需要实现任何协议和类似协议,则可以选择使用映射

另一个是,像艾伦·汤普森的解决方案一样,做一个安全的函数。
prismatic/pipeoping
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}

谢谢我将暂时不讨论这个问题,因为我希望找到一个不需要包括第三方的解决方案。