Hi everyone,

I'm starting to get familiar with clojure.spec, and in my very first spec I needed to specify relationship between the args themselves (similar to how :fn specs allow for specifying some relationship between :args & :ret). Is that at all possible? Here is my use-case for the sake of argument:

(defn extract
"Analogous to `clojure.core/get`, but returns a vector of `[item-at-k, coll-without-k]`. For Sequential things <k> can be an integer (the index), or a predicate. In case of a predicate, the first item that satisfies it will be extracted." [k coll]
  ...)

The implementation is quite simple but irrelevant for my question and therefore omitted. Here are some sample invocations:

(extract :a {:a 1 :b 2}) => [1 {:b 2}]

(extract :a #{:a :b}) => [:a #{:b}]

(extract 1 [:a :b :c]) => [:b [:a :c]]

(extract (partial = b) [:a :b :c]) => [:b [:a :c]] ;; same as above

(extract 3 [:a :b :c]) => [nil [:a :b :c]] ;; nothing found

And here is my attempt at spec-ing this:

(spc/def ::predicate (spc/fspec :args (spc/cat :x any?)
             :ret boolean?))

(spc/fdef extract

  :args (spc/cat :value (spc/alt :index nat-int?
                          ;:key any? FIXME::predicate ::predicate )
                 :coll coll?)

  :ret (spc/tuple any? coll?)

  :fn (spc/or :some-found #(let [[e c] (:ret %)
                           arg-coll (-> %:args :coll)]
                      (and (some? e)
                           ( > (count arg-coll)
                              (count c))))
        :nil-found #(let [[e c] (:ret %)
                          arg-coll (-> %:args :coll)]
                     (and (nil? e)
                          ( > (count arg-coll)
                             (count c))))
        :not-found #(let [[e c] (:ret %)
                          arg-coll (-> %:args :coll)]
                     (and (nil? e)
                          (= arg-coll c))))
  )


So, as you can probably see, there are 2 problems with this:

1) Even though, I've verified that ::predicate gens correct predicates (via `s/exercise`), when i try to gen-test it, it finds predicates which are causing `extract` to return something like `[x (x)]` (where x can be anything). So, it seems that there exist predicates that cause `extract` to find the item, but not remove it from coll. This is something that i can't reproduce manually!

2) You may have noticed a `FIXME` in the :args spec. I would like to enumerate the 3 possible/logical types of `:value (nat-int? or ::predicate for sequentials, but `any?` for maps/sets). If i uncomment what i currently have i can see that gen-testing will eventually mix something that is not an index nor a ::predicate (e.g. a string) with something sequential, which is not supposed to happen.

So basically I'm stuck with this. If i comment out the `:predicate ::predicate` entry, then it passes gen-testing, but actually it has only really tested 1/3 of the possible intended usages. :( Ok, you might say that integers are perfectly valid keys in maps or elements in sets, and so perhaps one could claim that 2/3 have been tested. Is there any way of fully spec-ing this fn, or should i just stick to a good doc-string and manually crafted test-cases?

Thanks in advance - any feedback is greatly appreciated :)

Kind regards,

Dimitris


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to