Fork me on GitHub

Good evening! I've written specs to validate a small math language with addition and multiplication. Now I want to generate values! But I'm hitting Stack Overflow. I assume that's because my specs/generators are mutually recursive. Any tips for writing mutually recursive spec generators that don't blow the stack? Code attached.


Idea: write generators as functions rather than directly on specs. Argument: recursion depth. Call generator functions from the spec.


Followed through on a generator namespace with recursion limit. That fixed my StackOverflow. Sample for add and multiply:

(defn add [depth]
  (gen/fmap to-list
            (gen/tuple (gen/return '+) (expression (dec depth)) (expression (dec depth)))))

(defn expression [depth]
  (if-not (pos? depth)
    (gen/one-of [(number)
                 (multiply depth)
                 (add depth)])))

🎉 3

Note: Tree depth can also be controlled probabilistically. Make it more probable to take the non-recursive path than the recursive path. Example from the docs:

(gen/frequency [[9 gen/small-integer] [1 (gen/return nil)]])
The probability of the non-recursive path would have to counterbalance the fanout for the recursive paths.


From reading test.check source, I see that many generators accept a size parameter. I suspect that I could have used that instead of inventing my own depth.


Grasp now supports finding keywords using clojure specs while also preserving location metadata! This should come in handy for finding re-frame events and subscriptions in CLJS apps. I also made a #grasp channel

🎉 9