Stream: beginners

Topic: Random


view this post on Zulip Kiryl Dziamura (Feb 03 2024 at 03:15):

What is the state of random generators?

I’ve started working on a library inspired by the rand crate. It’s monadic (chainable via andThen so the state is hidden), generic (provides abstractions via abilities (and I must say abilities in roc just awesome! Very smooth and intuitive experience!) so it’s possible to implement an arbitrary prng). It can also generate a list of bytes which might be useful in combination with the decoding ability. It’s only standard distribution for now, but yeah, it can be configured.

So, I’m planning to prepare a minimal poc. But maybe there are already developments on the same concept? I saw roc-random and elm/random but they’re not quite what I want.

view this post on Zulip Luke Boswell (Feb 03 2024 at 03:43):

We have https://github.com/lukewilliamboswell/roc-random

view this post on Zulip Luke Boswell (Feb 03 2024 at 03:44):

Sounds good :smiley: I'm not sure how much time Jan has had recently. It would be good to have another random library

view this post on Zulip Luke Boswell (Feb 03 2024 at 03:44):

I'm tracking anyone else working on random things

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 03:48):

I also have made some fuzzer and adjacent random library stuff

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 03:50):

But yeah, I think it would be easy to take some things like random. Make an ability for the prng (so the algorithm can be swapped out). Use monads and generators to get nice chaining and be able to generate any arbitrary type that implements an ability.

I think anyone can make a library like this without too much effort. Though without automatic derivation, and random generate isn't quite as awesome for arbitrary types.

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 03:51):

So lots of options for what can be done and I think good apis shouldn't be too hard to build

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 03:51):

The main disadvantage of any pure random solution like this is that it won't compare with tasks.

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 03:52):

So a lot of platforms may want to implement random generations as tasks for composibility reasons...though maybe there is a smart solution here.

view this post on Zulip Richard Feldman (Feb 03 2024 at 04:00):

regarding tasks, I think #API Design > Random number generation via Task is a nice design once module params land! :smiley:

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 04:02):

Just checking I understand it. Seed with a Task. Then for any use of it even though it is technically pure and can't fail, call Task.ok generateValue such that the random generator makes tasks and is composable.

view this post on Zulip Richard Feldman (Feb 03 2024 at 04:06):

yeah basically you have something platforms can opt into supporting, which is "I, as the platform, will track the current seed in state that lives in the platform, and whenever you run one of these random-number-generating Tasks, I'll give you a new seed by stepping my old one and then continuing to persist it"

view this post on Zulip Richard Feldman (Feb 03 2024 at 04:07):

so it would be an effect; the stored seed in the platform would change

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 06:59):

Ah yeah, I guess it wouldn't just seemless change with a random library cause the randomly library not only needs to return wrap the output in tasks, but every intermediate prng state much be gotten via tasks for it to be seamless

view this post on Zulip Anne Archibald (Feb 03 2024 at 10:37):

I wrote a super quick draft of a random generator (based on xorshift) with an iterator sort of interface.
One consideration I had in mind is seeding: building repeatable code (easily) is really useful for debugging randomised algorithms (for example monte carlo calculations or machine-learning training runs). A feature I haven't seen but would love to is quick efficient "branching" of random generators. That is, if I'm writing a function that receives a random generator as an argument and calls three different functions that each need a random generator, I want to spawn a new random generator off the current one and pass eac function a separate generator. That way each gets its own repeatble sequence, and changes to one function can't affect the sequence the others receive.

This does require randomness properties that are not usually tested for, but if it were a usable way of writing code I could imagine coming up with adequate generators.

view this post on Zulip Kiryl Dziamura (Feb 03 2024 at 13:32):

If I understand you correctly, then that's exactly what I’m working on. For my purpose, I need exactly controllable prngs and not a single one somewhere on the platform side.

Yeah, it’s important to have an entropy source provided by the platform. But for me, it sounds like a special case of the high-level api for managing tasks, at the abstract level randomness is just a stream of bits. Also, sometimes it’s enough to just retrieve the seed from the platform to pass it then as an initialization for a pure prng

view this post on Zulip Anne Archibald (Feb 03 2024 at 14:29):

The interface I was aiming for only makes sense if we have iterators/lazy lists (which I'm trying to write), so you could write something like:

\generator ->
{before: [a, b], after: rest} = Iterator.split generator 2
(f unc1 (Random.fromSeed a)) + (func2 (Random.fromSeed b)) + (func3 rest)

but the point is, func1 and func2 do all their work on fresh random streams. I don't know how often one will want to successively pull values from the generator within one function, but I suppose a good interface for doing that for random numbers will possibly also work for more general streams of values. (The exception is that other, non-random, streams can run out.)

view this post on Zulip Anne Archibald (Feb 03 2024 at 14:41):

Unlike more side-effect-friendly languages, truly random numbers need to have a different interface in roc from pseudo-random numbers. Which is maybe actually good, since people who need truly random numbers generally need them for cryptography, where they wouldn't want to slip up and accidentally use pseudo-random numbers?

For my Monte Carlo stuff I picture (if not debugging) using a Task to request enough randomness from the platform to construct a pseudo-random generator and then pulling everything from the generator. If debugging. I'd want to pull the seed from the source code or from a config file or something.

The exception to this approach is arguably the most common case: if I don't actually care whether my numbers are truly random but I don't want to be passing a generator object everywhere. This is the common situation, for example with python's random.random() or C's rand(). In other languages one can even fudge the debugging situation by seeding the global random number generator with a deterministic value, but this is fraught with challenges - any reordering or skipping or additional work risks getting the global generator out of sync and scrambling your numbers. I think the approach roc could take here would be to suggest platforms provide a Task that returns true random numbers, a Task that seeds a global pseudo-random number generator (might as well be the platform's native one I guess), and a Task that returns values from the global generator. It does force the user to use Tasks every time they want randomness, but that's required by roc's purity.

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 16:34):

I'm a bit confused by your use of true random numbers. Most people don't have hardware to generate true random numbers. Also, I would assume most platform random number generators will just be calling simple prngs.

I would expect some platforms to use cryptographically secure prngs, but that still isn't true random number generation.

view this post on Zulip Anne Archibald (Feb 03 2024 at 17:51):

Brendan Hansknecht said:

I'm a bit confused by your use of true random numbers. Most people don't have hardware to generate true random numbers. Also, I would assume most platform random number generators will just be calling simple prngs.

I would expect some platforms to use cryptographically secure prngs, but that still isn't true random number generation.

Linux provdes /dev/random, which is supposed to gather entropy from unpredictable physical processes the OS has access to - precise timings of mouse movements, keystrokes, disk access times, network packets, ... - and produce genuinely random numbers. Many Intel machines actually have hardware designed to generate large volumes of genuinely random bits, but there is a certain amount of distrust in the cryptographic community for these. In any case I really do mean true randomness here.

Cryptographically secure PRNGs need not be platform-dependent; I would be very happy to see stream ciphers and the like available to all roc programs.

view this post on Zulip Anne Archibald (Feb 03 2024 at 17:57):

Most standard-library pseudo-random number generators make use of the small amount of randomness available from reading the system clock on startup to ensure that they don't produce the same stream every time. Here is an example suggesting people do this: https://farside.ph.utexas.edu/teaching/329/lectures/node25.html

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 17:58):

I guess when I hear "true" random number generator, I think of the custom hardware for random number generation. Generally they are based on quantum mechanics nowadays. /dev/random is reasonable especially on user machines and probably good enough in general, but it is not based on a truly random device. Especially if you are thinking of an isolated server without input (though in practice, that is a rare case, you aways at least have network).

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 17:59):

Also, the totally entropy in /dev/random can be used up technically or hit other issues, but yeah probably totally good enough for 100% of use cases.

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 18:01):

Just to be clear, /dev/random is labeled as: cryptographically secure pseudorandom number generators (it just attempts to understand when entropy is low and block waiting on more entropy)

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 18:05):

Found this curious:

Unless you are doing long-term key generation (and most likely
not even then), you probably shouldn't be reading from the
/dev/random device or employing getrandom(2) with the GRND_RANDOM
flag.

Linux docs really don't suggest the use of /dev/random: https://man7.org/linux/man-pages/man7/random.7.html

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 18:07):

Anyway, that was all a side tagent. Yeah, cryptographically secure prng that is continuously seeded with assumed to be random entropy from various hardware interactions/environment. As such, it is a perfectly good source of random data.

view this post on Zulip Brendan Hansknecht (Feb 03 2024 at 18:09):

Most standard-library pseudo-random number generators make use of the small amount of randomness available from reading the system clock on startup to ensure that they don't produce the same stream every time.

Yeah, for most use cases, very simple prng + a simple seed on time is plenty random enough. Nowadays, pcg or xoroshiro for really fast implementation.


Last updated: Jul 05 2025 at 12:14 UTC