Does something like that make sense?
(defmulti dispatch identity)
(defmethod dispatch :default [v]
(:spec v))
(s/valid?
(s/multi-spec dispatch :spec)
{:spec (s/keys :req-un [::a ::b]) :a 1 :b 2})
=> true
The real code in :default branch would be more complex, but the idea is that I have a complex rule to infer spec from the value, and I don't want to encode it in a single predicate that executes the rule and calls s/valid? inside because it will lose the error reporting/explanation. I.e. I don't want to do this:
(s/valid?
(s/spec #(s/valid? (:spec %) %))
{:spec (s/keys :req-un [::a ::b]) :a 1})
=> false
because (s/explain-data ...) will just say that (s/valid? (:spec %) %) is false and not that the :b is missingExplanation comparison:
(s/explain
(s/spec #(s/valid? (:spec %) %))
{:spec (s/keys :req-un [::a ::b]) :a 1})
=> {:spec ... :a 1} - failed: (valid? (:spec %) %)
(s/explain
(s/multi-spec dispatch :spec)
{:spec (s/keys :req-un [::a ::b]) :a 1})
=> {:spec ... :a 1} - failed: (contains? % :b)Any opinions?
check this out:
(defmacro def-dynamic-spec [name & fn-tail]
(let [multi-name (symbol (str name "-multi-fn"))]
`(do
(defmulti ~multi-name (constantly nil))
(defmethod ~multi-name nil ~@fn-tail)
(def ~name (s/multi-spec ~multi-name nil)))))
a macro that creates a spec that dynamically infers a value spec using a function of the value, while preserving error reporting/explanation data