Fork me on GitHub
#clojure-spec
<
2022-10-14
>
Brian Beckman19:10:22

OBSERVATION: I noticed a handy feature: specs created via s/with-gen can automatically filter out nil. Is it generally true that such specs never produce nil when the spec-part of s/with-gen forbids nil? I guess, stated that way, it’s obvious that it should be generally true. This is a nice feature for me because my project generators create trees with level-crossing semantical constraints that can go wrong in combinatorial ways. Any time anything goes wrong (e.g., 0 to a negative power somewhere in the tree), I just barf out nil as if in a maybe monad (it’s about 2% of the time, so it’s fine). It’s nice that I get automatic filtering of the generated values at the spec level. Actually, it’s brilliant! It let me strip out the maybe monad from my code (simplifying it greatly) and just rely on nil punning and some-> and some->> operations. Consider the following:

(s/def ::nil-producing-spec
  (s/or :nil nil? :int integer?))

(def nil-producing-generator
  (s/gen ::nil-producing-spec))

(gen/sample nil-producing-generator)
;; => (nil nil -1 -2 nil 6 3 nil nil nil)

(s/def ::nil-rejecting-spec
  (s/with-gen
    integer?
    (fn [] nil-producing-generator)))

(gen/sample (s/gen ::nil-rejecting-spec))
;; => (0 -4 -2 -5 0 -64 0 -4 6 0)

lassemaatta02:10:49

I think s/gen checks the generated values and verifies they pass the spec (https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/alpha.clj#L287). You could swap integer? with something like pos-int? and see that it now filters out more than just nils.

👍 1
ghadi18:10:32

spec derived generators automatically filter spec derived values by the validity of the spec

ghadi18:10:22

this means that a great technique is to attach a sloppy generator to a specific spec

👍 1
ghadi18:10:04

e.g. give a generator that generates all dates to a spec/predicate for holidays