Random Functional Values
Functional random values, or sequences of well-spread numbers, are often a better fit than more sophisticated randomness, especially in functional programs.
Functional random values, or a sequence of well-spread values, is often a good choice over more advanced Randomness. They are side-effect-free, repeatable, and rely on a single state that’s easy to store and pass around as part of your program.
We could use a java.util.Random
sequence with an explicit seed like:
(defn randoms [seed]
(let [r (java.util.Random. seed)]
(repeatedly #(.nextLong r))))
But architectural principles suggest that state should contain simple, serializable data. A serialized java.util.Random
contains more than just a seed.
Instead, we just need a sequence that is repeatable and appears random. If each new value is the seed for the next, we only need to store a single value.
George Marsaglia’s fast random algorithm (http://www.jstatsoft.org/article/view/v008i14) fits perfectly:
(defn reasonable-spread [^long i]
(let [xor-right (fn [x n] (bit-xor x (bit-shift-right x n)))
xor-left (fn [x n] (bit-xor x (bit-shift-left x n)))]
(if (= 0 i)
1
(-> (xor-left i 21)
(xor-right 35)
(xor-left 4)))))
By feeding the result of reasonable-spread
back into itself, we generate a deterministic, well-spread pseudo-random sequence. A long
value is easy to pass around in most serialization formats.
For convenience, here’s how to produce an infinite sequence:
(defn reasonable-spreads [^long i]
(iterate reasonable-spread (reasonable-spread i)))
The result is a functional-style approach to randomness: deterministic, side-effect-free, and simple to serialize or replay. A perfect fit for functional programming patterns.