Fork me on GitHub
#graalvm
<
2020-08-29
>
borkdude18:08:10

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

borkdude18:08:06

I think a solution would be to wrap the global random object in a delay

lread19:08:13

Huh, interesting. Sorry no better ideas here but I suppose a fork could become a PR for test.check.

borkdude19:08:22

Trying alter-var-root first. If it works out, a proper PR could be made

borkdude19:08:44

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

borkdude19:08:09

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

borkdude19:08:50

@alexmiller Let me know if a PR is in order

borkdude19:08:21

I guess we could use clojure.core/force to work with normal values after the initial delay has been forced.

borkdude20:08:23

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

borkdude20:08:18

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)