Fork me on GitHub
#clojure-spec
<
2022-10-11
>
Brian Beckman20:10:20

Dear friends: Seeking explanation. I noticed that certain with-gen specs describe as unknown. To wit:

(s/def ::foo (s/with-gen (fn [x] (integer? x))
               (fn [] (gen/return 42))))
(s/valid? ::foo 1234)
;; => true
(gen/generate (s/gen ::foo))
;; => 42
(s/describe ::foo)
;; => :clojure.spec.alpha/unknown
But here is one that works:
(s/def ::bar (s/with-gen integer?
               (fn [] (gen/return 43))))
(s/valid? ::bar 2345)
;; => true
(gen/generate (s/gen ::bar))
;; => 43
(s/describe ::bar)
;; => integer?
What gives, please & thanks?

Alex Miller (Clojure team)20:10:09

(fn [x] (integer? x)) is an anonymous function, which is just an opaque object so there is no way in Clojure to work backward from that to a form

Alex Miller (Clojure team)20:10:59

with things like integer? we are working some magic to see that's a function tied to a var and demunging a class name to recover it

Alex Miller (Clojure team)20:10:55

in spec 2, we are able to get rid of all of that and better capture forms by having well separated symbolic and object layers, so eventually this will be improved

👍 1
Brian Beckman21:10:44

That makes sense, Alex. ty. I did notice that sometimes (specifically when there is no with-gen) describe can report an anonymous function:

(s/def ::baz (fn [x] (integer? x)))
(s/describe ::baz)
;; => (fn [x] (integer? x))

Alex Miller (Clojure team)21:10:37

in that case, s/def is a macro and can capture the form

👍 1
Alex Miller (Clojure team)21:10:45

with-gen is a function and the args are evaluated before invocation

Alex Miller (Clojure team)21:10:57

there is another option too that is a macro and can specify a custom generator:

user=> (s/def ::foo (s/spec (fn [x] (integer? x)) :gen (fn [] (gen/return 42))))
:user/foo
user=> (s/describe ::foo)
(fn [x] (integer? x))

Alex Miller (Clojure team)21:10:36

(although this is a different known problem in that it omits the custom gen)

👍 1
Brian Beckman22:10:54

great stuff. tyvm