[m-users.] Random Number Generator

Mark Brown mark at mercurylang.org
Mon Jul 31 09:46:29 AEST 2023


Hi Volker,

On Sun, Jul 30, 2023 at 9:17 PM Volker Wysk <post at volker-wysk.de> wrote:
>
> Hi
>
> I find the documentation of the random* modules a litte confusing. I got
> this far (this works):
>
> main(!IO) :-
>     open_system_rng(MaybeHandle, !IO),
>     (
>         MaybeHandle = maybe.ok(Handle),
>
>         random.system_rng.generate_uint64(Handle, First, !IO),
>         random.system_rng.generate_uint64(Handle, Second, !IO),
>         random.system_rng.generate_uint64(Handle, Third, !IO),
>
>         random.sfc64.seed(First, Second, Third, P, State),
>         make_io_urandom(P, State, M, !IO),
>
>         generate_uint64(M, R, !IO),
>         io.format("R=%s\n", [s(string.string(R))], !IO)
>     ;
>         MaybeHandle = maybe.error(Msg),
>         io.format("Error opening system random number generator:\n%s\n",
>                   [s(Msg)], !IO)
>     ).
>
>
> I don't like the argument M of make_io_random/5 and generate_uint64/4, which
> needs to be passed around when using the random number generator. The RNG is
> attached to the IO state only half-way, the M arument still needs to be
> passed around. I'd like to have some predicate "generate_uint64(uint64::out,
> io::di, io::uo)", which doesn't need the M argument, because all is stored
> in the IO state.

In C terms, this is akin to having access to a global resource (e,g,,
/dev/urandom), but having your code pass around the file handle with
which the process accesses it. You could avoid passing around the
handle by using a global static variable if you wanted.

Similarly, in Mercury you could use a module-local mutable variable to
store the system_rng handle, once initialized.

>
> Actually, that M argument is of type params and this is a dummy type (from
> the source code of random.sfc64):
>
> :- type params
>     --->    params.
>
> It's the same for random.sfc32, but not for random.sfc16. And the module
> random.sfc64 defines this:
>
> :- pred generate_uint64(uint64::out, ustate::di, ustate::uo) is det.
>
> That's looks like what I need, but there isn't such a member of the urandom
> type class, which I need to use when I want it to be attached to the IO
> state.

Since you're digging into the implementation, you could look at the
source code of make_io_urandom to see what's going on. It wraps the
ustate, which is destructively updatable because it is unique (it's an
array), in a mutvar, which is destructively updatable because it is
attached to the I/O state.

By "destructively updatable" I mean that there is some way for the
compiler to verify that such an update is safe. The two interfaces
provide two such options, though the actual update that is happening
is the same either way.

The random interface is designed to be general enough to allow for
different types of RNGs, since its purpose is to allow users to write
polymorphic code that is decoupled from the choice of RNG. It aims to
do this with a minimal amount of overhead, but we still end up with
some dummy types being passed around in some cases.

>
> I hope I've made sense.

You didn;t ask a specific question, but I hope I have been able to
provide some clarification.

Cheers,
Mark

>
>
> Cheers,
> Volker
> _______________________________________________
> users mailing list
> users at lists.mercurylang.org
> https://lists.mercurylang.org/listinfo/users


More information about the users mailing list