考虑以下类似
apply
的函数:
(defn apply [f v]
(f v))
我想以这样的方式指定这个函数,使其返回值满足与
f
的返回值相同的规范。此外,我还想指定输入值v
满足f
的输入规范。
我尝试指定类似的内容如下:
(s/fdef apply
:args (s/cat :f (s/fspec :args (s/cat :v any?) :ret any?))
:ret any?)
这个规范非常宽松。我想做的是量化两个谓词
returned?
和 argument?
以进一步约束规范。
(s/fdef apply
:args (s/cat :f (s/fspec :args (s/cat :v argument?) :ret returned?))
:ret returned?)
其中
argument?
和 returned?
表示两个任意谓词。据我了解,这种类型的量化在clojure.spec
中是不可能的。
更一般地,这种类型的行为可以使用任何参数类型系统自然地表达,包括 Java 的泛型。这也可以使用
clojure.spec
来指定吗?
您需要一些函数来确定输入参数的规格
f
。我不知道有任何这样的功能,所以这是一种可能性。是的,我知道,它很脏:
(require '[clojure.spec.alpha :as s]
'[clojure.spec.test.alpha :as stest])
(defn find-fn-spec [f]
(first
(for [[fvar fns] (-> #'stest/instrumented-vars deref deref)
:when (or (= f (:wrapped fns))
(= f (:raw fns)))]
(#'s/reg-resolve! (symbol fvar)))))
然后你可以使用该函数来编写我称之为
my-apply
的其他函数的规范(核心中已经有一个函数 apply
):
(defn my-apply [f v]
(f v))
(s/fdef my-apply
:args (s/and (s/cat :f fn? :x any?)
(fn [input]
(let [{:keys [f x]} input]
(s/conform (:args (find-fn-spec f)) [x]))))
:ret any?
:fn (fn [{:keys [args ret]}]
(s/valid? (:ret (find-fn-spec (first args))) ret)))
但我不建议你这样做...