On Thursday, November 3, 2016 at 1:12:39 PM UTC-5, Jim foo.bar wrote:
>
> 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? 
>

:fn is the best way to do this (specifying a relationship either between 
args or between an arg and ret)
 

> 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!
>

Is the info on the failing example in this case not sufficient enough to 
determine the failing case? I think it's worth at least considering the 
possibility that your code has a bug. :) Maybe the info is not sufficient, 
but I can judge without seeing the output and the actual code.
 

> 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). 
>

I don't think it's a good idea to separate the nat-int? and any? cases - I 
would just use any? in this case. The key here (and really everywhere 
you're writing an arg spec) is to try to state the truth as much as you 
can. The truth here is that anything can be a key (even though there is an 
identifiable case where the keys happen to be ints).
 

> 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? 
>

You should definitely spec it! But this is a case where a custom generator 
is called for - in particular one that takes into account a better model 
for the inputs to the function. Stu Halloway did a whole screencast on this 
at 
https://www.youtube.com/watch?v=WoFkhE92fqc and I would recommend fully 
understanding that approach. The gist is that instead of generating the key 
to find and the collection independently, you want to instead create a 
model of multiple cases. When you're trying to model the case where a key 
is found, don't generate it randomly - instead generate the coll, and use 
one of its keys.

Hope that helped.
 

> 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