Clojure destructure map使用:具有合格关键字的键不起作用

时间:2016-02-27 11:32:00

标签: clojure

我已经尝试过1.7.0和1.8.0,看起来Clojure没有使用其键完全限定的:keys来解构地图。我不认为它与参数的尾部有关,因为当我切换函数参数位置时它不起作用。

(ns foo.sandbox)

(def foo ::foo)
(def bar ::bar)

(defn normalize-vals
  [mmap & [{:keys [foo bar] :as ops}]]
  (println "normalize-vals " ops " and foo " foo " bar" bar))

(normalize-vals {} {foo 1 bar 2})

=> normalize-vals  {:foo.sandbox/foo 1, :foo.sandbox/bar 2}  and foo  nil  bar  nil

然而;这有效:

    (defn normalize-vals
      [mmap & [{a foo b bar :as ops}]]
      (println "normalize-vals " ops " and foo " a " bar" b))

    (normalize-vals {} {foo 1 bar 2})

=> normalize-vals  {:cmt.sandbox/foo 1, :cmt.sandbox/bar 2}  and foo  1  bar  2

这是一个缺陷吗?

2 个答案:

答案 0 :(得分:1)

让我们按原样执行您的功能:

(defn normalize-vals
  [mmap & [{:keys [foo bar] :as ops}]]
  (println "normalize-vals " ops " and foo " foo " bar" bar))

请注意foo&上面的bar是本地绑定,它们不引用函数之外的任何东西。

...并稍微重写其他代码:

(def foo-const ::foo)
(def bar-const ::bar)      

不要过分关注这里的命名,重点是使用不同的名称。

(normalize-vals {} {foo 1 bar 2})
;; error: ...Unable to resolve symbol: foo in this context...

(normalize-vals {} {foo-const 1 bar-const 2})
;; prints: normalize-vals  {:user/foo 1, :user/bar 2}  and foo  nil  bar nil

课程应该是尽可能使用唯一的名称。

为什么第二种变体有效?

以解构形式{a foo b bar :as ops};

  • a& b是新的本地绑定。如果我们有一个名为ab的var,则会在此函数的范围内覆盖它们。
  • foo& bar已从环境中解析出来。如果我们不使用上面的-const后缀,我们会得到CompilerException,就像上面那样。

答案 1 :(得分:0)

您使用非限定关键字进行解构,因此不是:

[mmap & [{:keys [foo bar] :as ops}]]

你应该使用

[mmap & [{:keys [::foo ::bar] :as ops}]]

您可以使用clojure.walk/macroexpand-all展开normalize-vals

(clojure.walk/macroexpand-all '(defn normalize-vals
                                 [mmap & [{:keys [foo bar] :as ops}]]
                                 (println "normalize-vals " ops " and foo " foo " bar" bar)))

=> (def normalize-vals (fn* ([mmap & p__26720] (let* [vec__26721 p__26720 map__26722 (clojure.core/nth vec__26721 0 nil) map__26722 (if (clojure.core/seq? map__26722) (. clojure.lang.PersistentHashMap create (clojure.core/seq map__26722)) map__26722) ops map__26722 foo (clojure.core/get map__26722 :foo) bar (clojure.core/get map__26722 :bar)] (println "normalize-vals " ops " and foo " foo " bar" bar)))))

需要注意的扩展的重要部分是:

foo (clojure.core/get map__26722 :foo) 
bar (clojure.core/get map__26722 :bar)

因此,地图解构中的键将转换为编译时的关键字,并且不会使用命名空间中foo和bar vars的值。