我可以使用 Clojure 规范验证函数吗?

Can I validate functions with Clojure spec?

我可以使用 Clojure 规范系统来定义函数签名并验证函数是否满足它们吗?

以下是我尝试过但没有成功的一些例子

(s/valid? (s/fspec :args string? :ret string?) identity) ;; false

(def nope identity)
(s/valid? (s/fspec :args string? :ret string?) nope) ;; false

(s/conform (s/fspec :args string? :ret string?) identity) ;; invalid

(defn strmap [s] {:pre [(s/valid? string? s)] :post [(s/valid? string? %)]} s)
(s/valid? (s/fspec :args string? :ret string?) strmap) ;; false

(s/fdef strmap :args string? :ret string?)
(s/valid? strmap strmap) ;; true

(s/def ::str-pred (s/fspec :args string? :ret boolean?))
(s/valid ::str-pred (fn [s] true)) ;; false

我知道 fdef,但我想要一些我可以创作的东西。例如,创建相关函数签名的映射。

clojure.specmalli is a data-driven schema library that has support for function schemas

的替代方法

您可以使用 s/get-spec 访问函数的 :args:ret 规范,然后验证:

user=> (require '[clojure.spec.alpha :as s])
nil
user=> (defn strmap [s] s)
#'user/strmap
user=> (s/fdef strmap :args string? :ret string?)
user/strmap
user=> (s/valid? (:args (s/get-spec `strmap)) "foo")
true
user=> (s/valid? (:args (s/get-spec `strmap)) :bar)
false

我在 re-find.web 中使用它。

事实证明 spec 确实处理函数,但它期望参数是一个元组。

(s/valid? (s/fspec :args (s/cat :arg1 string?) :ret string?) identity) ;; true!
(s/valid? (s/fspec :args (s/cat :arg1 string?) :ret string?) (fn [x] (str x "hi there"))) ;; true
(s/valid? (s/fspec :args (s/cat :arg1 string?) :ret string?) (fn [x] x)) ;; true

(s/valid? (s/fspec :args (s/cat :arg1 string?) :ret string?) (fn [x] 42)) ;; false

匿名函数文字似乎确实存在一些问题。我可能对宏扩展有一些误解。

(s/valid? (s/fspec :args (s/cat :arg1 string?) :ret string?) #(%)) ;; false