Fork me on GitHub
#clojure-spec
<
2018-10-24
>
misha01:10:37

I think it fails to create generator within s/*recursion-limit*

misha01:10:24

(dotimes [_ 100]
  (binding [s/*recursion-limit* 10]
    (count (gen/sample (s/gen ::nested-associative) 100))))
=> nil

(dotimes [_ 100]
  (binding [s/*recursion-limit* 1]
    (count (gen/sample (s/gen ::nested-associative) 100))))
=>
CompilerException clojure.lang.ExceptionInfo: Unable to construct gen at: [:coll :branch :map 1 :branch] for: :scratch/nested-associative #:clojure.spec.alpha{:path [:coll :branch :map 1 :branch], :form :scratch/nested-associative, :failure :no-gen}

misha02:10:27

tried 10000 times, limit=9 threw in a ~20 seconds, limit=10 ran for minutes, had to kill it opieop

misha02:10:22

so, you need to add non-recursive clause to s/or, so it can be chosen when recursion-limit is reached:

(s/def ::nested-associative
  (s/or
    :leaf string?
    :coll (s/coll-of ,,,)
    :map  (s/map-of ,,,)))

taylor02:10:37

@bmaddy I’m able to get that to reliably generate with s/*recursion-limit* 1 by pulling the recursive s/or branch into its own spec:

(s/def ::branch
  (s/or :leaf string?
        :branch ::nested-associative))
(s/def ::nested-associative
  (s/or :coll (s/coll-of ::branch
                         :min-count 0
                         :max-count 1
                         :gen-max 1)
        :map (s/map-of keyword? ::branch
                       :min-count 0
                       :max-count 1
                       :gen-max 1)))
but I’m not sure why that is, maybe it’s the way that recursive depth is tracked internally and the “inline” recursive s/or specs lose some context on recursion?

taylor02:10:12

(dotimes [_ 1000]
  (binding [clojure.spec.alpha/*recursion-limit* 1]
    (dorun (gen/sample (s/gen ::nested-associative) 100))))

misha03:10:40

this is the first thing I did, and it still failed for me numerous times, @taylor

taylor03:10:40

:man-shrugging: works on my machine

bmaddy04:10:50

Oh, I get it now. I'd tried setting s/*recursion-limit* to a low number before to help avoid StackOverflow issues, but that's actually the problem here. When the recursion limit is hit, it must throw that error. Thanks @misha and @taylor!

basti15:10:50

Is there a strict validation mode in spec? Whereas if you provided more data then fail?

dadair15:10:35

No; closed specs are generally discouraged. You can do your own strictness using s/and, however.

bbrinck19:10:30

@basti spell-spec gives you options for either keeping the map open (but reporting likely misspelled keys) or complete closed maps (not recommended) https://github.com/bhauman/spell-spec