Stream: platform development

Topic: roc-random record builder


view this post on Zulip Luke Boswell (Oct 14 2024 at 01:51):

Needed to generate a random list of buildings for a background... Sam helped upgrade roc-random to use records builders... and now I can do this which is cool.

randomBuilding : Random.Generator U32 { x : F32, y : F32, width : F32, height : F32, color : Color }
randomBuilding =

    updateY = \values -> {values & y : screenHeight - 130f32 - values.height}

    { Random.chain <-
        x: Random.static 0f32,
        y: Random.static 0f32,
        width: Random.u32 50 200 |> Random.map Num.toF32,
        height: Random.u32 100 800 |> Random.map Num.toF32,
        color: {
            Random.chain <-
                red: Random.u32 200 240 |> Random.map Num.intCast,
                green: Random.u32 200 240 |> Random.map Num.intCast,
                blue: Random.u32 200 250 |> Random.map Num.intCast,
        } |> Random.map \{ red, green, blue } -> RGBA red green blue 255,
    }
    |> Random.map updateY

Screenshot 2024-10-14 at 12.51.46.png

view this post on Zulip Richard Feldman (Oct 14 2024 at 01:52):

that's cool! :smiley:

view this post on Zulip Richard Feldman (Oct 14 2024 at 01:52):

why all the |> Random.map Num.intCast? :thinking:

view this post on Zulip Richard Feldman (Oct 14 2024 at 01:52):

feels like that shouldn't be necessary

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:53):

That's something that would be good to investigate. I think it comes down to Roc having 13? different number types, and interfaces needing to handle every one separately

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:54):

So I think it's happening because the original author didn't feel like copying the same functions 13 times

view this post on Zulip Richard Feldman (Oct 14 2024 at 01:54):

oh ok

view this post on Zulip Richard Feldman (Oct 14 2024 at 01:54):

yeah I think copying them is the way to go :big_smile:

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:54):

Weaver is like 60% this type of code, and 40% everything else haha

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:54):

Not that it matters that much

view this post on Zulip Luke Boswell (Oct 14 2024 at 01:55):

I thought it was because I'm mixing U32 and U8 for the seed/state here... but I don't fully grok it

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:55):

But I can foresee ecosystem devs getting lazy in the same way

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:55):

I'll see if I can remove them

view this post on Zulip Richard Feldman (Oct 14 2024 at 01:55):

there should be a Random.u8, Random.i8, Random.u16, Random.i16, ...etc

view this post on Zulip Richard Feldman (Oct 14 2024 at 01:55):

so you don't have to map that

view this post on Zulip Luke Boswell (Oct 14 2024 at 01:56):

There is, but that changes the state type, and I don't think we can then mix them

view this post on Zulip Luke Boswell (Oct 14 2024 at 01:56):

https://lukewilliamboswell.github.io/roc-random/Random/#u8

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:56):

Oh, good point...

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:56):

Yeah, the seed is kept as the same as the generated int

view this post on Zulip Luke Boswell (Oct 14 2024 at 01:57):

Tbh, I'm not sure why there is different state types in here

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:57):

And this composition API only works if the seeds are the same

view this post on Zulip Luke Boswell (Oct 14 2024 at 01:57):

I think it's because there is 32/16/8 bits of "noise"

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:57):

I presume we can just make all of the seeds Num *

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:57):

And let Roc's "default num type" mechanism figure it out for the user

view this post on Zulip Luke Boswell (Oct 14 2024 at 01:58):

That would be a nice upgrade

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:58):

PR #2, here we come

view this post on Zulip Luke Boswell (Oct 14 2024 at 01:59):

── TYPE MISMATCH in examples/2d_camera.roc ─────────────────────────────────────

Something is off with the body of the randomBuilding definition:

59│   randomBuilding : Random.Generator (Num *) { x : F32, y : F32, width : F32, height : F32, color : Color }
60│   randomBuilding =
61│
62│       updateY = \values -> {values & y : screenHeight - 130f32 - values.height}
63│
64│>      { Random.chain <-
65│>          x: Random.static 0f32,
66│>          y: Random.static 0f32,
67│>          width: Random.int 50 200 |> Random.map Num.toF32,
68│>          height: Random.int 100 800 |> Random.map Num.toF32,
69│>          color: {
70│>              Random.chain <-
71│>                  red: Random.u8 200 240,
72│>                  green: Random.u8 200 240,
73│>                  blue: Random.u8 200 250,
74│>          } |> Random.map \{ red, green, blue } -> RGBA red green blue 255,
75│>      }
76│>      |> Random.map updateY

This Random.map call produces:

    Generator U32 { … }

But the type annotation on randomBuilding says it should be:

    Generator (Num *) { … }


── TYPE MISMATCH in examples/../../roc-random/package/Random.roc ───────────────

Something is off with the body of the u8 definition:

201│  u8 : U8, U8 -> Generator (Num *) U8
202│  u8 = \x, y -> betweenUnsigned x y
                    ^^^^^^^^^^^^^^^^^^^

This betweenUnsigned call produces:

    State (Int Unsigned8) -> {
        state : State (Int Unsigned8),
        value : Int Unsigned8,
    }

But the type annotation on u8 says it should be:

    State (Num *) -> {
        state : State (Num *),
        value : U8,
    }

────────────────────────────────────────────────────────────────────────────────

2 errors and 0 warnings found in 16 ms

I actually.. I don't think that works either

view this post on Zulip Notification Bot (Oct 14 2024 at 01:59):

27 messages were moved here from #platform development > roc-ray by Luke Boswell.

view this post on Zulip Sam Mohr (Oct 14 2024 at 01:59):

Let me try

view this post on Zulip Luke Boswell (Oct 14 2024 at 02:02):

I'm thinking we should make the 32-bit variant the default, so we can have the Random.u8 do what you expect. But then separate out the 16/8-bit noise ones into separate modules like (Random16, Random8) so people can still use those if they need the lower noise.

Then every function within a module will play nicely together.

view this post on Zulip Sam Mohr (Oct 14 2024 at 02:02):

I think that would work, but I'm not sure why leaving it generic wouldn't also work

view this post on Zulip Sam Mohr (Oct 14 2024 at 02:03):

And it would lead to lower surface area

view this post on Zulip Luke Boswell (Oct 14 2024 at 02:04):

I think it's probably that bug, where once you use a top-level it's type becomes fixed/specialised or something like that

view this post on Zulip Sam Mohr (Oct 14 2024 at 02:05):

Ah...

view this post on Zulip Sam Mohr (Oct 14 2024 at 02:05):

And based on what Ayaz said, I don't expect that we'll be changing how that works any time soon

view this post on Zulip Richard Feldman (Oct 14 2024 at 02:06):

ah yeah that's true

view this post on Zulip Richard Feldman (Oct 14 2024 at 02:06):

separate functions seems fine though

view this post on Zulip Richard Feldman (Oct 14 2024 at 02:06):

probably less error-prone to use too

view this post on Zulip Sam Mohr (Oct 14 2024 at 02:06):

Because apparently the alternative is that much slower to compile

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:08):

So I think that roc-random is unintentionally confusing as an api. It is trying to support all of the different PCG variants with all of the various state types.

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:09):

Almost no one ever wants or needs that

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:09):

You really just want the recommended 64 bit version hard coded

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:09):

Then you want to map from the values it returns into the various smaller integer types.

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:09):

If you are generating 8 bit numbers you don't want an 8 bit internal state.

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:10):

You still want the full 64 bit internal state

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:11):

Even on a 32 bit system, it is probably recommended to generally use the 64 bit state. 32 bits is not actually that much randomness and both will still be quite fast. Most rngs have 256 or way more state. The most common mersenne twister has 4kb of state if I recall corectly.

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:18):

Yeah, just looked at the pcg library again. For a simple pcg library, I think it is recommended to just implement either the 32bit version (which has 64 bits of state) or the 64bit version (which has 128 bits of state).

view this post on Zulip Luke Boswell (Oct 14 2024 at 03:24):

Sounds good. I've just ripped the other variants out. I'll see if I can upgrade to U64 as the state... maybe U32 is fine, considering we will have builtin Crypto module which provides better crypto, this is just for randomish numbers

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:26):

U64 is a safer bet IMO

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:27):

So it is either U128 for state with U64 output or U64 as state with U32 as output. I think either is fine desktop. For embedded, 32 bit, or weaker devices, the small state runs a lot faster.

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:28):

The former is my vote

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:28):

I think the former is the better default as well.

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:28):

And yes, you're right. We should go for the "sane defaults, complex available" route

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:28):

I think it would be fine to support both as well. But I wouldn't support any state sizes besides those two.

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:29):

And just expose the U128/U64 variant under something like Random and U64/U32 under Random.U32

view this post on Zulip Luke Boswell (Oct 14 2024 at 03:30):

I think I'd like to try renaming the Random.chain function to Random.generate... just because it "feels" right

view this post on Zulip Luke Boswell (Oct 14 2024 at 03:30):

Messing with the documentation

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:33):

I'd not vote for generate

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:33):

There are lots of candidates, but I'd only consider ones that also work if you're not using a record builder

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:33):

Unless you have a cool enough name, of course

view this post on Zulip Luke Boswell (Oct 14 2024 at 03:35):

Screenshot 2024-10-14 at 14.35.43.png

view this post on Zulip Luke Boswell (Oct 14 2024 at 03:36):

Yeah, it might be a little confusing...

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:38):

Does that need to return a generator? Couldn't it return an rgb directly?

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:39):

I think record builders should be flexible enough to support that, right?

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:39):

It can do either

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:39):

That said, if it generates an RGB direcltly, where does the state go?

view this post on Zulip Sam Mohr (Oct 14 2024 at 03:39):

The problem with generating values is that you don't actually generate the value in this version of the library, you generate the value and the state, yes

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:40):

Yeah, so I guess you have to build up a generator (theoretically could do that with decode)... Though I think it requires the future version of decode.

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:40):

Anyway, that is a side track for now.

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:41):

I think a nice record builder generator api and a separate direct api sounds great.

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 03:42):

Also, I would save Random.generate for converting a Generator into the final value?

view this post on Zulip Luke Boswell (Oct 14 2024 at 04:00):

Would you expect Random.u8 to generate a random U8 between Num.maxU8 and Num.minU8 or would you expect it to be a function that takes an upper and lower bound? say u8 : U8, U8 -> Generator U8

view this post on Zulip Sam Mohr (Oct 14 2024 at 04:02):

The former.

view this post on Zulip Sam Mohr (Oct 14 2024 at 04:02):

I'd usually want:

view this post on Zulip Luke Boswell (Oct 14 2024 at 04:05):

Ok, so both in an API then

view this post on Zulip Luke Boswell (Oct 14 2024 at 04:06):

Also, I'm super tempted to generate a list of numbers and plot these on a histogram... would you expect a uniform distribution from PCG? I'm not super familiar with these things

view this post on Zulip Sam Mohr (Oct 14 2024 at 04:07):

According to Wikipedia, PCGs are equidistributed, so yes

view this post on Zulip Luke Boswell (Oct 14 2024 at 04:11):

Screenshot 2024-10-14 at 15.11.31.png

Looks alright to me

view this post on Zulip Luke Boswell (Oct 14 2024 at 04:34):

Ok, https://github.com/lukewilliamboswell/roc-random/pull/10

I did a big cleanup... ran out of steam and I'll leave the upgrade to U64 state for another time. This is working well for what I need, and I spent most of my time improving the docs, examples and API so it's much easier to use. The state upgrade should be all internal so that shouldn't really affect anyone.

view this post on Zulip Brendan Hansknecht (Oct 14 2024 at 05:17):

Yeah, I know early roc-random had a bug at least with calculating the bounds when limiting. Cause when plotted as a scatter plotted, it clearly sampled zero too much and missed the lower edge. But I think that was fixed


Last updated: Jul 05 2025 at 12:14 UTC