Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by

(Also see closed http://dev.clojure.org/jira/browse/CLJ-1964)

(require '[clojure.spec :as s]) (s/def ::map-tree (s/map-of keyword? (s/or :tree ::map-tree :leaf nil?))) (s/exercise ::map-tree)

hangs on my machine.

Another example from https://groups.google.com/forum/#!topic/clojure/IvKJc8dEhts, which immediately results in a StackOverflowError on my machine:

(require '[clojure.spec.gen :as gen])

(defrecord Tree [name children])
(defrecord Leaf [name])

(s/def ::name string?)
(s/def ::children (s/coll-of (s/or :tree ::Tree, :leaf ::Leaf)))

(s/def ::Leaf (s/with-gen

            (s/keys :req-un [::name])
            #(gen/fmap (fn [name] (->Leaf name)) (s/gen ::name))))

(s/def ::Tree (s/with-gen

            (s/keys :req-un [::name ::children])
               (fn [[name children]] (->Tree name children))
               (s/gen (s/tuple ::name ::children)))))

;; occasionally generates but usually StackOverflow
(binding [s/*recursion-limit* 1]

(gen/generate (s/gen ::Tree)))


clojure.lang.RT.seqFrom (RT.java:533)
clojure.lang.RT.seq (RT.java:527)
clojure.core/seq--6221 (core.clj:137)
clojure.core/map/fn--6687 (core.clj:2736)
clojure.lang.LazySeq.sval (LazySeq.java:40)
clojure.lang.LazySeq.seq (LazySeq.java:49)
clojure.lang.RT.seq (RT.java:525)
clojure.core/seq--6221 (core.clj:137)
clojure.core/every? (core.clj:2652)
clojure.spec/tuple-impl/reify--13509 (spec.clj:905)
clojure.spec/gensub (spec.clj:228)
clojure.spec/gen (spec.clj:234)


5 Answers

0 votes

Comment made by: lgs32a

As the author of CLJ-1964 I can't confirm this.

(binding [s/*recursion-limit* 1] (s/exercise ::map-tree))

... immediately generates.

Using the new :gen-max argument spec can also generate with a higher recursion limit in reasonable time

(s/def ::map-tree (s/map-of keyword? (s/or :tree ::map-tree :leaf nil?)

                        :gen-max 3))

(time (s/exercise ::map-tree))
"Elapsed time: 0.135683 msecs"

Note that :gen-max defaults to 20, so with 4 recursion steps this quickly ends up generating 20^5 3.2 million values

0 votes

Comment made by: alexmiller

I tried this again today and the first example still works just fine for me. I'm using Java 1.8 with default settings in a basic Clojure repl (not lein).

0 votes

Comment made by: mtruyens

With the :gen-max option, everything works now. Thanks for the suggestion!

0 votes

Comment made by: kenran

The first example works fine for me as well, but I'm having a problem mostly similar to the second one. The following code runs into a StackOverflow pretty quickly unless I set :gen-max or :max-count to a low value (e.g. 2):

(require '[clojure.spec.alpha :as s])
(require '[clojure.spec.gen.alpha :as gen])

(s/def ::bar string?)
(s/def ::baz (s/coll-of (s/or :s string? :b ::foo)))
(s/def ::foo

 (s/keys :req [::bar ::baz]))))

(gen/generate (s/gen ::foo))

The s/spec (or s/with-gen, which I also tried) seem to be the problem here, because the following works well and should produce equivalent data:

(s/def ::bar2 string?)
(s/def ::baz2 (s/coll-of (s/or :s2 string? :b2 ::foo2)))
(s/def ::foo2 (s/keys :req [::bar2 ::baz2]))

(gen/generate (s/gen ::foo2))

I'm using java 10.0.2 64 bit, but a colleague can reproduce it with the latest java 8.

Am I using it incorrectly or is there a problem hidden somewhere?

0 votes
Reference: https://clojure.atlassian.net/browse/CLJ-1978 (reported by alex+import)