clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
hadils 2021-01-30T17:17:15.007100Z

Is there any documentation on clojure.spec.alpha.gen (spec2)?

seancorfield 2021-01-30T17:40:58.007700Z

@hadilsabbagh18 Spec 2 is not ready for use yet. It's still evolving and it has a number of bugs right now.

hadils 2021-01-30T17:41:19.008Z

Thanks @seancorfield

seancorfield 2021-01-30T17:43:15.008500Z

(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/ )

hadils 2021-01-30T18:27:13.009700Z

Thanks @alexmiller -- I have already read those websites. I was asking about gen specifically.

alexmiller 2021-01-30T18:30:24.010100Z

No changes from spec 1

alexmiller 2021-01-30T18:30:49.010300Z

So far

hadils 2021-01-30T18:57:19.011300Z

@alexmiller -- my IDE indicates differently. I am requiring clojure.alpha.spec.gen -- e.g. it does not have fmap

alexmiller 2021-01-30T18:59:38.011600Z

That’s doesn’t seem right

alexmiller 2021-01-30T19:01:45.012Z

I think your ide is wrong :)

hadils 2021-01-30T19:02:15.012300Z

Ok.

alexmiller 2021-01-30T19:03:05.013Z

No changes here from spec 1

alexmiller 2021-01-30T19:03:32.013800Z

But it is generating those defs as wrappers so a static analyzer wouldn’t see them

alexmiller 2021-01-30T19:03:55.014400Z

If you load the ns you should see those vars though

hadils 2021-01-30T19:05:06.014900Z

I have a different sha. Let me try yours and see what happens...

hadils 2021-01-30T19:11:07.015400Z

I tried it and it works now. Thanks for all your help.

2021-01-30T21:01:14.020100Z

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?

2021-02-01T15:52:20.021700Z

@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.

2021-02-01T15:53:33.022100Z

Cool. Glad you are finding ways to get the behavior you are hoping for.

2021-02-01T15:55:30.022300Z

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).

2021-01-30T21:27:07.020200Z

Oh, ok, I just realised that I get better results when I’m calling gen/generate. Weird. 🤷

2021-01-30T22:09:39.020800Z

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.

2021-01-30T22:10:28.021Z

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.

2021-01-30T22:32:49.021200Z

Could be, I don’t know. I end up creating custom integers generator using MersenneTwister PRNG.

2021-01-30T22:36:11.021400Z

(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])