Repository navigation
Add an API to serialise/deserialise to/from disk #123
Description
Activity
This is a reasonable request and in fact I wanted it myself for some time. However, I think it would be better to provide a general interface for this, in case other PRNGs need such functionality. Maybe something along the lines of:
class RandomGen g where data Seed g :: Type toSeed :: g -> Seed g fromSeed :: Seed g -> g ...
this would allow for
StdGenimplementation to beinstance RandomGen StdGen where data Seed StdGen = StdGenSeed !Word64 !Word64 toSeed (StdGen smGen) = uncurry StdGenSeed $ SM.unseedSMGen smGen fromSeed (StdGenSeed seed gamma) = StdGen $ SM.seedSMGen seed gamma ...
Thoughts?
Reacted by Peter Kjeld AndersenYes, I think that something along those lines should work, thanks!
What do you actually gain from adding this to
RandomGen, i.e. what is the conceptual difference betweengandSeed g? I suppose it would make it possible to write code that is polymorphic in e.g. aSerialize (Seed g)constraint for someSerializeclass, but you could just as well useSerialize gdirectly.EDIT: I suppose it could be useful if instead of an associated data family,
toSeed/fromSeedactually converted to some kind of primitive representation (e.g. some flavour of byte string).@adamgundry You are right, the suggested interface is not powerful enough to be useful.
It would be nice to have general ability to initialize any PRNG from a seed, say if we were to provide some ability to draw entropy from the system in a form of a
ByteString. For that we would need information fromgon how many bytes it needs.Another thing we'd like, as this ticket suggest, is the ability to serialize any
gto file and back. However, asking for just a string of bytes is not sufficient in my books, we need some type safety. How about this for an addition to the interface:newtype BytesN (n :: Nat) = BytesN ShortByteString toBytesN :: forall n. KnownNat n => ShortByteString -> Maybe (BytesN n) fromBytesN :: BytesN n -> ShortByteString class RandomGen g where type SeedSize g :: Nat toSeed :: g -> BytesN (SeedSize g) fromSeed :: BytesN (SeedSize g) -> g
This would allow serialization libraries to provide instances for any PRNG regardless of the underlying representation. But most importantly it would allow us to make it opt in and off by default, making it backwards compatible:
class RandomGen g where type SeedSize g = TypeError (ShowType g :<>: Text " doesn't support saving seeds") toSeed :: g -> BytesN (SeedSize g) toSeed _ = error "Impossible: Not supported" fromSeed :: BytesN (SeedSize g) -> g fromSeed _ = error "Impossible: Not supported"
which would produce a type error on
toSeed/fromSeedinstead of some ugly runtime error.For anyone interested in this functionality there is an implementation in #162 that is still lacking some tests, but already works quite nicely. Here is an example function that should depict the power of this new
SeedGeninterface quite nicely:random/src/System/Random/Seed.hs
Lines 245 to 251 in 5fb946b
withSeedFile :: (SeedGen g, MonadIO m) => FilePath -> (g -> m (a, g)) -> m a withSeedFile fileName f = do bs <- liftIO $ BS.readFile fileName seed <- liftIO $ mkSeedFromByteString bs (res, gen) <- f $ seedGen seed liftIO $ BS.writeFile fileName $ unSeedToByteString $ unseedGen gen pure res Reacted by Alfredo Di Napoli
Up until
random-1.1a barebone (and potentially brittle) way to serialise and deserialise aStdGenwould have been to useshowandread, howeverrandom-1.2.0removedRead(for good reasons) which means this is not possible anymore. As a consequence, writing things like aSerializeinstance forStdGenis not possible at the moment.Technically speaking one can work around this by using the
.Internalmodule and simply useseedSMGen'andunseedSMGenon the underlyingSMGen, but it feels wrong to use theInternalmodule and to rely on the concrete implementation ofStdGen.In a nutshell, it would be nice to have two functions as part of the API similar to the following:
Thanks!