clojure.spec regression bug for 1.9.0-alpha6

567 views
Skip to first unread message

Alan Thompson

unread,
Jun 14, 2016, 8:01:09 PM6/14/16
to clo...@googlegroups.com
Hi - Just noticed that the :ret function in fdef seems to be ignored in 1.9.0-alpha6:

user=> (require '[clojure.spec :as s])
user=> (defn dummy [x] (if x "yes" "no"))
user=> (s/fdef dummy
  #_=>   :args (s/cat :x integer?)
  #_=>   :ret  integer?)
user=> (s/instrument #'dummy)
user=> (dummy 3) (println *clojure-version*)
ExceptionInfo Call to #'user/dummy did not conform to spec:
val: "yes" fails at: [:ret] predicate: integer?
:clojure.spec/args  (3)
  clojure.core/ex-info (core.clj:4703)
{:major 1, :minor 9, :incremental 0, :qualifier alpha5}

;-------------------------------------------------------------------

user=> (dummy 3) (println *clojure-version*)
"yes"
{:major 1, :minor 9, :incremental 0, :qualifier alpha6}

Alex Miller

unread,
Jun 14, 2016, 8:14:32 PM6/14/16
to Clojure
As noted in the alpha change list, this was an intentional change in what instrument does. Instrument is intended to be used to verify that other callers have invoked a function correctly. Checking that the function works (by verifying that :ret and :fn return valid results) should be done using one of the spec.test functions during testing.

Some other spec features are still to be added as well that relate to this change.

webber

unread,
Jun 15, 2016, 1:30:55 PM6/15/16
to Clojure
Maybe, the following is the same reason.

```
(defn ranged-rand   ;; BROKEN!
  "Returns random integer in range start <= rand < end"
  [start end]
  (+ start (rand-int (- start end))))

(s/fdef ranged-rand
  :args (s/and (s/cat :start integer? :end integer?)
               #(< (:start %) (:end %)))
  :ret integer?
  :fn (s/and #(>= (:ret %) (-> % :args :start))
             #(< (:ret %) (-> % :args :end))))
(ranged-rand 8 10)
;;=> ExceptionInfo Call to #'spec-example.core/ranged-rand did not conform to spec:
;;=> At: [:fn] val: {:args {:start 8, :end 10}, :ret 7} fails predicate: (>= (:ret %) (-> % :args :start))
;;=> :clojure.spec/args  (8 10)
;;=>   clojure.core/ex-info (core.clj:4617)
(println *clojure-version*)
;;=> {:major 1, :minor 9, :incremental 0, :qualifier alpha1}

(s/instrument #'ranged-rand)
(ranged-rand 8 10)
;;=> 7
(println *clojure-version*)
;;=> {:major 1, :minor 9, :incremental 0, :qualifier alpha6}
```

Alex Miller

unread,
Jun 15, 2016, 2:33:55 PM6/15/16
to Clojure
I'm in the process of updating that part of the guide right now, should be done by end of day.

Sean Corfield

unread,
Jun 15, 2016, 3:54:27 PM6/15/16
to Clojure Mailing List

Given that we now have to use clojure.spec.test to get :ret / :fn tested, we lose the “nice” exceptions explaining the conformance failure:

 

Alpha 5:

 

;;=> ExceptionInfo Call to #'spec-example.core/ranged-rand did not conform to spec:

;;=> At: [:fn] val: {:args {:start 8, :end 10}, :ret 7} fails predicate: (>= (:ret %) (-> % :args :start))

;;=> :clojure.spec/args  (8 10)

;;=>   clojure.core/ex-info (core.clj:4617)

 

Alpha 6:

 

boot.user=> (t/check-var #'ranged-rand)

{:result {:clojure.spec/problems {[] {:pred (>= (:ret %) (-> % :args :start)), :val {:args {:start -1, :end 2}, :ret -2}, :via [], :in []}}, :failed-on :fn}, :seed 1466016600676, :failing-size 4, :num-tests 5, :fail [(-1 2)], :shrunk {:total-nodes-visited 5, :depth 1, :result {:clojure.spec/problems {[] {:pred (>= (:ret %) (-> % :args :start)), :val {:args {:start -1, :end 1}, :ret -2}, :via [], :in []}}, :failed-on :fn}, :smallest [(-1 1)]}}

 

Are there plans to provide an “explain” equivalent for this?

 

Sean Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alex Miller

unread,
Jun 16, 2016, 9:33:45 AM6/16/16
to Clojure
You haven't pretty-printed it to look very nice, but I think all of the same information (and more due to shrinking) is still in the check-var output. I don't know of any plan to add what you're asking for beyond what's below.

{:result
 {:clojure.spec/problems
  {[]
   {:pred (>= (:ret %) (-> % :args :start)),
    :val {:args {:start -1, :end 2}, :ret -2},
    :via [],
    :in []}},
  :failed-on :fn},
 :seed 1466016600676,
 :failing-size 4,
 :num-tests 5,
 :fail [(-1 2)],
 :shrunk
 {:total-nodes-visited 5,
  :depth 1,
  :result
  {:clojure.spec/problems
   {[]
    {:pred (>= (:ret %) (-> % :args :start)),
     :val {:args {:start -1, :end 1}, :ret -2},
     :via [],
     :in []}},
   :failed-on :fn},
  :smallest [(-1 1)]}}

Sean Corfield

unread,
Jun 16, 2016, 1:52:12 PM6/16/16
to Clojure Mailing List

I fear you’re missing my point.

 

You can get close to the previous nice value with:

 

(#’s/explain-out (:result (t/check-var  #’ranged-rand)))

 

But that leverages a private function / implementation detail and doesn’t handle :failed-on very nicely:

 

boot.user=> (#'s/explain-out (:result (t/check-var #'ranged-rand)))

val: {:args {:start -3, :end 1}, :ret -5} fails predicate: (>= (:ret %) (-> % :args :start))

:failed-on  :fn

 

When you run-all-tests you get the non-pretty-printed version as a hard-to-read blob of text on stdout and a bare pass/fail map result. Having that text formatted via something like explain-out would be a big help for usability when testing. Having that function exposed publicly would be a nice convenience for other tooling to build on top of clojure.spec.test.

 

We can pass :reporter-fn to check-var / check-fn, but we run-all-tests just passes println in and calls (prn ret) on the result of check-var so we have no control over that output.

 

Sean Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

 

--

Alex Miller

unread,
Jun 17, 2016, 2:11:35 PM6/17/16
to Clojure
explain-out is now public in master. Further mods may be coming re testing.


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+unsubscribe@googlegroups.com.

Nikita Prokopov

unread,
Jul 5, 2016, 9:43:31 AM7/5/16
to Clojure
How to I make spec/fdef and regular clojure.test (not generative ones) work together? I’d like for my :ret specs and HOF :ret specs to be checked during regular tests. Is that possible?

Alex Miller

unread,
Jul 5, 2016, 10:19:37 AM7/5/16
to Clojure
There is nothing provided or planned to do that. 
Reply all
Reply to author
Forward
0 new messages