How to check the resolvability of Clojure specs?
1 | (s/def :foo/bar (s/or :nope :foo/foo)) |
在这里,
1 2 | (s/valid? :foo/bar 42) ;; Exception Unable to resolve spec: :foo/foo clojure.spec.alpha/reg-resolve! (alpha.clj:69) |
当我输入
深入研究
1 2 3 4 5 6 7 8 9 10 11 12 | (ns my-ns.spec-test (:require [clojure.test :refer :all] [clojure.spec.alpha :as s] ;; :require all the relevant namespaces to populate the ;; global spec registry. [my-ns.spec1] [my-ns.spec2])) (deftest resolvable-specs (doseq [spec (keys (s/registry))] (is (resolvable? spec)))) ;; ^^^^^^^^^^^ placeholder; thata€?s the function I want |
不幸的是,这不是
1 2 3 4 5 6 7 8 9 10 11 12 | (s/def :int/int int?) (s/def :bool/bool bool?) (s/def :my/spec (s/or :int :int/int :other (s/or :bool bool/bool :nope :idont/exist))) (s/valid? :my/spec 1) ; <- matches the :int branch ;; => true (s/valid? :my/spec :foo) ;; Exception Unable to resolve spec: :idont/exist clojure.spec.alpha/reg-resolve! (alpha.clj:69) |
我检查了异常堆栈跟踪以及源代码,以查看是否可以找到任何功能来完全解析规格,而无需使用上面的
对于给定的规范,是否有办法检查它在其所有分支中引用的所有规范是否存在?
我能够执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | (ns my-ns.utils (:require [clojure.spec.alpha :as s])) (defn- unresolvable-spec [spec] (try (do (s/describe spec) nil) (catch Exception e (if-let [[_ ns* name*] (re-matches #"Unable to resolve spec: :([^/]+)/(.+)$" (.getMessage e))] (keyword ns* name*) (throw e))))) (defn unresolvable? "Test if a spec is unresolvable, and if so return a sequence of the unresolvable specs it refers to." [spec] (cond (symbol? spec) nil (keyword? spec) (if-let [unresolvable (unresolvable-spec spec)] [unresolvable] (not-empty (distinct (unresolvable? (s/describe spec))))) (seq? spec) (case (first spec) or (->> spec (take-nth 2) rest (mapcat unresolvable?)) and (->> spec rest (mapcat unresolvable?)) ;; undecidable nil) :default (unresolvable-spec spec))) (def resolvable? (complement unresolvable?)) |
它可以在
1 2 3 | (u/resolvable? :int/int) ;; => true (u/resolvable? :my/spec) ;; => false (u/unresolvable? :my/spec) ;; => (:idont/exist) |
但是它有一些缺陷:
-
它重新发明了轮子;我认为这些规范遍历功能已经存在于
clojure.spec.alpha 中 -
它依赖于捕获异常然后解析其消息,因为(1)
clojure.spec.alpha 不具有不会引发异常的函数,并且(2)引发一个异常的函数不使用任何更具体的函数比Exception
如果有人有更强壮的东西,我很乐意接受其他答案。