I'm doing an experiment of integrating clojure.spec into babashka, together with generators. I'm running into a slight problem: the generated results aren't random, due to this line being evaluated at compile time, I think: https://github.com/clojure/test.check/blob/7e7c2116fa721211e8f5642a249e4a0f327445f4/src/main/clojure/clojure/test/check/random.clj#L175 One solution is to fork clojure.test.check, unless someone may have a better idea... I could also try alter-var-root
I think a solution would be to wrap the global random object in a delay
Huh, interesting. Sorry no better ideas here but I suppose a fork could become a PR for test.check.
Trying alter-var-root first. If it works out, a proper PR could be made
Seem to work :)
borkdude@DESKTOP-JN2UNTV ~/dev/babashka (spec) $ ./bb "(require '[clojure.spec.alpha :as s] '[clojure.spec.gen.alpha :as gen]) (s/def ::foo int?) (gen/generate (s/gen ::foo))"
-2
borkdude@DESKTOP-JN2UNTV ~/dev/babashka (spec) $ ./bb "(require '[clojure.spec.alpha :as s] '[clojure.spec.gen.alpha :as gen]) (s/def ::foo int?) (gen/generate (s/gen ::foo))"
-11621
borkdude@DESKTOP-JN2UNTV ~/dev/babashka (spec) $ ./bb "(require '[clojure.spec.alpha :as s] '[clojure.spec.gen.alpha :as gen]) (s/def ::foo int?) (gen/generate (s/gen ::foo))"
-13
The patch:
(def next-rng
"Returns a random-number generator. Successive calls should return
independent results."
(let [a (atom (delay (r/make-java-util-splittable-random (System/currentTimeMillis))))
thread-local
(proxy [ThreadLocal] []
(initialValue []
(delay
(first (r/split (swap! a #(second (r/split (deref %)))))))))]
(fn []
(let [rng @(.get thread-local)
[rng1 rng2] (r/split rng)]
(.set thread-local (delay rng2))
rng1))))
(defn make-random
"Given an optional Long seed, returns an object that satisfies the
IRandom protocol."
([] (next-rng))
([seed] (r/make-java-util-splittable-random seed)))
(alter-var-root #'r/next-rng (constantly next-rng))
(alter-var-root #'r/make-random (constantly make-random))
@alexmiller Let me know if a PR is in order
I guess we could use clojure.core/force
to work with normal values after the initial delay has been forced.
So this works with GraalVM as well:
(def next-rng
"Returns a random-number generator. Successive calls should return
independent results."
(let [a (atom (delay (r/make-java-util-splittable-random (System/currentTimeMillis))))
thread-local
(proxy [ThreadLocal] []
(initialValue []
(first (r/split (swap! a #(second (r/split (force %))))))))]
(fn []
(let [rng (.get thread-local)
[rng1 rng2] (r/split rng)]
(.set thread-local rng2)
rng1))))
(defn make-random
"Given an optional Long seed, returns an object that satisfies the
IRandom protocol."
([] (next-rng))
([seed] (r/make-java-util-splittable-random seed)))
but wrapping the entire body of next-rng
into a delay and then calling (@next-rng)
in make-random
doesn't (I got: can't case Delay into IFn with GraalVM compiled)