Search code examples
clojureclojure.spec

Clojure Spec and destructuring?


Not sure how to write a Spec to check the destructured arguments to a function.

If I have this simple function:

(defn make-name [[first last]]
  (str first " " last))

And I write this Spec for the argument:

(s/def ::vstring2 (s/coll-of string? :kind vector? :count 2 :distinct false))

It works correctly:

(s/conform ::vstring2 ["Rich" "Hickey"])
=> ["Rich" "Hickey"]

But when I try to apply it to the function like so:

(s/fdef make-name
        :args ::vstring2
        :ret string?)

It blows up:

(stest/check `make-name)
=>
({:spec #object[clojure.spec.alpha$fspec_impl$reify__2451
                0x7dd4c5ac
                "clojure.spec.alpha$fspec_impl$reify__2451@7dd4c5ac"],
  :clojure.spec.test.check/ret {:result #error{:cause "Wrong number of args (2) passed to: roster/make-name",

(...)

How do I write the function Spec using ::vstring2? Or do I?

Thank you.


Solution

  • You just need to specify another spec for your function's arguments e.g. using s/cat:

    (s/fdef make-name
      :args (s/cat :arg1 ::vstring2)
      :ret string?)
    

    In your example, the :args spec is expecting two arguments because your ::vstring2 spec is a collection expecting two strings. With this change, the function :args spec knows that it only takes one argument which should conform to ::vstring2.

    There are more function spec examples here and here.