Can I use the Clojure spec system to define function signatures and verify if functions satisfy them?
Here are some examples I've tried without success
(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
I know about fdef
, but I'd like something I can compose. For example, creating a map of related function signatures.
Turns out spec does handle functions, but it expects the arguments to be a tuple.
(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
There does appear to be some issues with the anonymous function literal. I'm probably misunderstanding something about the macro expansion.
(s/valid? (s/fspec :args (s/cat :arg1 string?) :ret string?) #(%)) ;; false