*Description*
If you instrument a function, you may get a spec error like the following:
(defn f [x] (inc x))
(s/fdef f
:args (s/cat :x (s/and integer? even?))
:ret (s/and integer? odd?))
(t/instrument)
(f 3)
;; ExceptionInfo Call to #'user/f did not conform to spec:
;; In: [0] val: 3 fails at: [:args :x] predicate: even?
;; :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__1200
0x19b3f9a "clojure.spec.alpha$regex_spec_impl$reify__1200@19b3f9a"]
;; :clojure.spec.alpha/value (3)
;; :clojure.spec.alpha/args (3)
;; :clojure.spec.alpha/failure :instrument
;; :clojure.spec.test.alpha/caller {:file "form-init3240393046310519022.clj", :lin
e 1, :var-scope user/eval1413}
;; clojure.core/ex-info (core.clj:4725)
(ex-data *e)
;; {:clojure.spec.alpha/problems
;; [{:path [:args :x],
;; :pred clojure.core/even?,
;; :val 3,
;; :via [],
;; :in [0]}],
;; :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x19b3f9a "clojure.spec.alpha$regex_spec_impl$reify__1200@19b3f9a"],
;; :clojure.spec.alpha/value (3),
;; :clojure.spec.alpha/args (3),
;; :clojure.spec.alpha/failure :instrument,
;; :clojure.spec.test.alpha/caller {:file "form-init3240393046310519022.clj", :line 1, :var-scope user/eval1413}}
As you can see,
- the explain-data has a regex (ie. the spec for the args of f) in it as {{::s/spec}}
- each problem contains {{:args}} in their {{:path}}
These facts can cause a confusion to spec error reporters because the spec for the args of {{f}} ({{(s/and integer? even?)}}) has no subspec corresponding to the key {{:args}} (I believe {{:path}} should only contains keys that is a clue to indicate which subspec to be chosen from a spec).
*Possible resolutions*
To resolve this confusing situation and improve the consistency of explain-data for instrument check, I think there are two options as follows:
- *Solution 1.* removing {{:args}} from {{:path}}
- *Solution 2.* modifying explain-data for instrument check so that they have fspec (rather than {{:args}} of it) as {{::s/spec}}
Personally, I prefer *Solution 2.* since adding fspec in explain-data makes it possible to provide richer error information to {{\*explain-out\*}} implementors.
The same goes for {{macroexpand-check}}.