Function suggestion: withFrozen
akhra opened this issue · comments
The pure System.Random interface is nice, but still considerably slower than working within the pcg-random idiom. Chasing an alternative, I've come up with this:
withFrozen :: (Variate a)
=> (forall s. Gen s -> ST s a) -> FrozenGen -> (a, FrozenGen)
withFrozen f g = runST $ flip runStateT g $ do
gen <- get >>= \frozen -> restore frozen
x <- lift $ f gen
save gen >>= \frozen -> put frozen
return x
By way of example, I've also got a function
roll :: (PrimMonad m) => Int -> Gen (PrimState m) -> m Int
and it works with either withFrozen (roll n) g
or withSystemRandom $ roll n
.
I'm sketchy on the order of arguments -- I also had the idea of working this into a StateT variant, but putting the function last would be more convenient in a lot of cases -- and make no guarantees as to my code being anywhere close to optimal, but it does work and it's MUCH faster than the System.Random interface.
Yeah, the System.Random
interface is unfortunately very slow because of how the class is defined. There's a GSOC project that will hopefully fix this.
There's no need for StateT
here, it's simply:
withFrozen :: (forall s. Gen s -> ST s a) -> FrozenGen -> (a, FrozenGen)
withFrozen f s = runST $ do
g <- restore s
a <- f g
s' <- save g
return (a, s')
or even
withFrozen f s = runST $ restore s >>= \g -> liftA2 (,) (f g) (save g)
which I'll add.
I've actually started a pure branch here which performs faster than using ST
. I couldn't figure out a nice interface and since I only use the ST
version I've just left it until the GSOC project comes.
Added in aa5b482. I decided to flip the arguments.
My predictions of suboptimality have yet to come up short. ;) Ed Kmett brought up GSOC yesterday on #haskell-game too, sounds like there's real momentum there.