(defn form-spec [spec err-format] (with-meta (s/spec spec) {:err-format err-format})) (s/def ::number (form-spec int? (fn [k v] (str (name k) " is not a valid number: " v)))) (s/def :address/apt (form-spec ::number (fn [_ v] (str "wrong apartment number: " v)))) (s/def :address/strno ::number) (s/def :address/street string?) (s/def ::addr (s/keys :req-un [:address/apt :address/street :address/strno])) (defn err [k v] (str (name k) " is invalid: " v)) (->> (s/explain-data ::addr {:apt "1" :street 23 :strno "stt"}) ::s/problems (map (fn [{:keys [path val via]}] (let [errfn (-> via last s/get-spec meta :err-format (or err))] (errfn (last path) val))))) ;; => ("wrong apartment number: 1" "street is invalid: 23" "strno is not a valid number: stt")