目前,Clojure的解构实现将从满足clojure.core/seq?的任何值创建一个HashMap。我认为这产生了不希望产生的副作用,使得在使用自定义的也是Seq的关联类型时无法使用解构。在尝试解构标记值类的实例时出现了这种情况,该实例在模式匹配时以[k v]的方式作为seq,但由于v已知是map,因此还关联在map部分,以避免更新保留标签时的语法开销。
;; 此类型的一个草图
(deftype ATaggedVal [t v]
clojure.lang.Indexed
(nth [self i] (nth self i nil))
(nth [self i o] (case i (0) t (1) v o))
clojure.lang.Sequential
clojure.lang.ISeq
(next [this] (seq this))
(first [this] t)
(more [this] (.more (seq this)))
(count [this] 2)
(equiv [this obj] (= (seq this) obj))
(seq [self] (cons t (cons v nil)))
clojure.lang.Associative
(entryAt [self key] (.entryAt v key))
(assoc [_ sk sv] (ATaggedVal. t (.assoc v sk sv)))
clojure.lang.ILookup
(valAt [self k] (.valAt v k))
(valAt [self k o] (.valAt v k o))
clojure.lang.IPersistentMap
(assocEx [_ sk sv] (ATaggedVal. t (.assocEx v sk sv)))
(without [_ sk] (ATaggedVal. t (.without v sk))))
因此,使用此类东西
(let [{:keys [x]} (ATaggedVal. :foo {:x 3 :y 4})] x)
;; 期望为3
=> nil
由于对于任何clojure.core/get将发挥作用的类型T,它应满足clojure.core/map?,因此只需简单地修改解构的行为,只在全球表中不满足map?时才构建HashMap。
附带的补丁实现了这一更改。