Is there any documentation on clojure.spec.alpha.gen (spec2)?
@hadilsabbagh18 Spec 2 is not ready for use yet. It's still evolving and it has a number of bugs right now.
Thanks @seancorfield
(and the only documentation for it is what you'll find in the repo and the auto-generated API docs: https://clojure.github.io/spec-alpha2/ )
https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha
https://github.com/clojure/spec-alpha2/wiki/Schema-and-select
Thanks @alexmiller -- I have already read those websites. I was asking about gen specifically.
No changes from spec 1
So far
@alexmiller -- my IDE indicates differently. I am requiring clojure.alpha.spec.gen
-- e.g. it does not have fmap
That’s doesn’t seem right
I think your ide is wrong :)
Ok.
No changes here from spec 1
But it is generating those defs as wrappers so a static analyzer wouldn’t see them
If you load the ns you should see those vars though
I have a different sha. Let me try yours and see what happens...
I tried it and it works now. Thanks for all your help.
Hi, I’m trying to create a time generator based on given time range. I thought about using s/int-in
but distribution for default generator for this spec is not very useful. E.g.
(gen/sample (s/gen (s/int-in 1 1000000000)))
=> (2 1 2 5 5 6 15 2 3 4)
(the range is from 1 to bilion while all sample results are leaning to beginning of the range)
Are there any built-in generators I could use that can give me better distributed results?@andy.fingerhut re your answer https://clojurians.slack.com/archives/C1B1BB2Q3/p1612044579020800?thread_ts=1612040474.020100&cid=C1B1BB2Q3: This is exactly the case I was experiencing two days ago. I didn’t understand how generators work. This talk was eye opening: https://www.youtube.com/watch?v=F4VZPxLZUdA.
Creating a custom generator was an overkill, simple gen/scale
was enough for what I needed.
Cool. Glad you are finding ways to get the behavior you are hoping for.
The "small is simpler and should be generated earlier" makes sense for some numeric inputs, e.g. if your Fibonacci function has bugs for small input values, you probably want to debug those first before figuring out why it fails when given 10,325 as input. But for your use case of date/time ranges, in most applications it seems like the values early in the range are no simpler than any others (I imagine there could be date/time use cases where just-inside and just-outside of acceptable ranges are important corner cases to ensure that you test).
Oh, ok, I just realised that I get better results when I’m calling gen/generate
. Weird. 🤷
I am not sure if what I am about to describe is what is happening in your case, but note that many spec generators try to generate "small test cases" first, then "larger/more complex test cases" only after those pass. For numbers, that sometimes means generating smaller values first, and only when those test cases pass, larger values. Default generators can be overridden if you don't like how they work.
The idea being if that "small" test cases fail, those are usually easier to debug the root cause and fix it, versus seeing large complex failing test cases.
Could be, I don’t know. I end up creating custom integers generator using MersenneTwister PRNG.
(defn gen-int-in
[{:keys [min max]}]
(gen/fmap
(fn [_] (-> (twist/next-int) (mod (- max min)) (+ min)))
(gen/return :something)))
(defn millis->date-time
[millis]
(-> millis
(Instant/ofEpochMilli)
(.atZone (ZoneId/systemDefault))
.toLocalDateTime))
(defn gen-local-date-time
[[from to]]
(gen/fmap
millis->date-time
(gen-int-in {:min from :max to})))
sample execution:
(gen/sample (gen-int-in {:min 1 :max 1000000000}) 15)
=>
(242825682
150977944
334804692
989355517
482024216
627890233
163007490
227494133
464164671
747363986
762489210
261499033
582274967
50926907
472313391)
(gen/sample (gen-local-date-time (create-future)))
=>
(#object[java.time.LocalDateTime 0x4139290 "2021-02-22T13:25:24.531"]
#object[java.time.LocalDateTime 0x53185984 "2021-01-31T00:17:03.320"]
#object[java.time.LocalDateTime 0xcb5632b "2021-02-23T02:22:41.585"]
#object[java.time.LocalDateTime 0x773ccacd "2031-01-23T16:49:36.048"]
#object[java.time.LocalDateTime 0x6fa351a8 "2021-02-09T10:45:48.003"]
#object[java.time.LocalDateTime 0x7ee6f1a1 "2031-01-29T13:07:57.477"]
#object[java.time.LocalDateTime 0x596d796d "2021-02-20T11:15:51.061"]
#object[java.time.LocalDateTime 0x4896aa2f "2031-01-18T13:37:21.066"]
#object[java.time.LocalDateTime 0x3dcd9c84 "2021-02-23T23:50:04.910"]
#object[java.time.LocalDateTime 0x746f0965 "2021-02-21T22:55:50.011"])
(`create-future` returns pair of timestamps [now, 10-years-from-now])