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.