Open adinapoli opened 2 years ago
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 StdGen
implementation to be
instance 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?
Yes, 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 between g
and Seed g
? I suppose it would make it possible to write code that is polymorphic in e.g. a Serialize (Seed g)
constraint for some Serialize
class, but you could just as well use Serialize g
directly.
EDIT: I suppose it could be useful if instead of an associated data family, toSeed
/fromSeed
actually 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 from g
on how many bytes it needs.
Another thing we'd like, as this ticket suggest, is the ability to serialize any g
to 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
/fromSeed
instead 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 SeedGen
interface quite nicely:
Up until
random-1.1
a barebone (and potentially brittle) way to serialise and deserialise aStdGen
would have been to useshow
andread
, howeverrandom-1.2.0
removedRead
(for good reasons) which means this is not possible anymore. As a consequence, writing things like aSerialize
instance forStdGen
is not possible at the moment.Technically speaking one can work around this by using the
.Internal
module and simply useseedSMGen'
andunseedSMGen
on the underlyingSMGen
, but it feels wrong to use theInternal
module 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!