摘要
1) 给定名为{{map-f}}的可测量函数,该函数被延迟调用,例如(def ls (map map-f (range)))。
2) 还有一个名为{{varargs-f}}的可变参数函数。当您将LazySeq传递给{{varargs-f}}时,一些或所有元素都会在符合规范时变为实际行动。
3) 问题:在调用{{(apply varargs-f ls)}}时,一些对{{map-f}}的无效调用可能会未被发现。
重现示例
在以下代码中,{{map-f}}预期在用除Symbol以外的其他类型调用时会抛出异常。然而,用String调用{{map-f}}时,会悄悄地通过。
`
(ns repro
(:require
[clojure.spec.alpha :as s]
[clojure.spec.test.alpha :as stest]))
(defn map-f [x]
(println "用类型调用my-fn" (type x))
(println (deref #'clojure.spec.test.alpha/instrument-enabled))
{x 1})
(s/fdef map-f :args (s/cat :x symbol?))
(defn varargs-f [& maps]
true)
(s/fdef varargs-f :args (s/cat :maps (s/* map?)))
(defn repro [& args]
(apply varargs-f (map map-f args)))
(stest/instrument)
(repro 'foo 'bar "baz")
`
输出
调用my-fn的类型clojure.lang.Symbol true 调用my-fn的类型clojure.lang.Symbol true 调用my-fn的类型java.lang.String ;; <-- nil
原因
当{{varargs-f}}的参数在契合规范后变为实际行动时,一些调用{{map-fn}}的操作在{{with-instrument-disabled}}的作用域内进行:https://github.com/clojure/spec.alpha/blob/f23ea614b3cb658cff0044a027cacdd76831edcf/src/main/clojure/clojure/spec/test/alpha.clj#L140
背景
我遇到这个问题时正在规范化{{merge-with}}。某些测试命名空间中的规范函数不再抛出异常,因为clojure.test中的这一行与上述规范的描述有相似的问题:https://github.com/clojure/clojure/blob/28efe345d5e995dc152a0286fb0be81443a0d9ac/src/clj/clojure/test.clj#L775
CLJ-2443.patch包含了一个修复,但我意识到它可能还不是完美无缺的。因此,我提供了一个只包含单元测试的CLJ-2443-test.patch,该单元测试可用于测试替代解决方案。