Ecosystem code
An immutable key does not subtype Random.AbstractRNG. If mutable code accepted a key, repeated calls could replay values without an error.
Adapt at the library boundary
Wrap a key or cursor when a library accepts Random.AbstractRNG.
using ImmutableRNGs, Random
function library_estimate(rng::Random.AbstractRNG, n)
noise = randn(rng, n)
weights = rand(rng, n)
return sum(noise .* weights) / n
end
key = Philox4x32(7)
value, cursor = mutably(key) do rng
library_estimate(rng, 128)
end
(value, cursor_position(cursor) > 0)(0.005665565167381887, true)mutably limits wrapper ownership to the call and returns the next cursor. Use it when the library controls its primitive draw count.
Continue from the returned cursor
Resume from the cursor rather than from the original key.
next_value, next_cursor = mutably(cursor) do rng
library_estimate(rng, 128)
end
replayed_value, replayed_cursor = mutably(cursor) do rng
library_estimate(rng, 128)
end
(next_value == replayed_value, next_cursor == replayed_cursor)(true, true)A fresh MutableRNG(key) restarts the sequence. A fresh MutableRNG(cursor) resumes it.
Separate concurrent chains first
Derive all chain keys before creating wrappers. Do not share a wrapper between active tasks.
chain_keys = splitrng(key, Val(4))
chain_values = map(chain_keys) do chain_key
library_estimate(MutableRNG(chain_key), 128)
end
length(unique(chain_values)) == length(chain_values)true