farlee2121
farlee2121

Reputation: 3337

Can I validate functions with Clojure spec?

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.

Upvotes: 0

Views: 698

Answers (3)

farlee2121
farlee2121

Reputation: 3337

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

Upvotes: 0

Michiel Borkent
Michiel Borkent

Reputation: 34790

You can access the :args and :ret spec of a function using s/get-spec and then validate:

user=> (require '[clojure.spec.alpha :as s])
nil
user=> (defn strmap [s] s)
#'user/strmap
user=> (s/fdef strmap :args string? :ret string?)
user/strmap
user=> (s/valid? (:args (s/get-spec `strmap)) "foo")
true
user=> (s/valid? (:args (s/get-spec `strmap)) :bar)
false

I am using this in re-find.web.

Upvotes: 4

Steffan Westcott
Steffan Westcott

Reputation: 2201

An alternative to clojure.spec, malli is a data-driven schema library that has support for function schemas

Upvotes: -1

Related Questions