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
that's cool! :smiley:
why all the |> Random.map Num.intCast
? :thinking:
feels like that shouldn't be necessary
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
So I think it's happening because the original author didn't feel like copying the same functions 13 times
oh ok
yeah I think copying them is the way to go :big_smile:
Weaver is like 60% this type of code, and 40% everything else haha
Not that it matters that much
I thought it was because I'm mixing U32 and U8 for the seed/state here... but I don't fully grok it
But I can foresee ecosystem devs getting lazy in the same way
I'll see if I can remove them
there should be a Random.u8
, Random.i8
, Random.u16
, Random.i16
, ...etc
so you don't have to map
that
There is, but that changes the state type, and I don't think we can then mix them
https://lukewilliamboswell.github.io/roc-random/Random/#u8
Oh, good point...
Yeah, the seed is kept as the same as the generated int
Tbh, I'm not sure why there is different state types in here
And this composition API only works if the seeds are the same
I think it's because there is 32/16/8 bits of "noise"
I presume we can just make all of the seeds Num *
And let Roc's "default num type" mechanism figure it out for the user
That would be a nice upgrade
PR #2, here we come
── 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
27 messages were moved here from #platform development > roc-ray by Luke Boswell.
Let me try
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.
I think that would work, but I'm not sure why leaving it generic wouldn't also work
And it would lead to lower surface area
I think it's probably that bug, where once you use a top-level it's type becomes fixed/specialised or something like that
Ah...
And based on what Ayaz said, I don't expect that we'll be changing how that works any time soon
ah yeah that's true
separate functions seems fine though
probably less error-prone to use too
Because apparently the alternative is that much slower to compile
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.
Almost no one ever wants or needs that
You really just want the recommended 64 bit version hard coded
Then you want to map from the values it returns into the various smaller integer types.
If you are generating 8 bit numbers you don't want an 8 bit internal state.
You still want the full 64 bit internal state
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.
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).
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
U64 is a safer bet IMO
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.
The former is my vote
I think the former is the better default as well.
And yes, you're right. We should go for the "sane defaults, complex available" route
I think it would be fine to support both as well. But I wouldn't support any state sizes besides those two.
And just expose the U128/U64 variant under something like Random
and U64/U32 under Random.U32
I think I'd like to try renaming the Random.chain
function to Random.generate
... just because it "feels" right
Messing with the documentation
I'd not vote for generate
There are lots of candidates, but I'd only consider ones that also work if you're not using a record builder
Unless you have a cool enough name, of course
Screenshot 2024-10-14 at 14.35.43.png
Yeah, it might be a little confusing...
Does that need to return a generator? Couldn't it return an rgb directly?
I think record builders should be flexible enough to support that, right?
It can do either
That said, if it generates an RGB direcltly, where does the state go?
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
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.
Anyway, that is a side track for now.
I think a nice record builder generator api and a separate direct api sounds great.
Also, I would save Random.generate
for converting a Generator
into the final value?
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
The former.
I'd usually want:
Random.u8
Random.boundedU8
Ok, so both in an API then
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
According to Wikipedia, PCGs are equidistributed, so yes
Screenshot 2024-10-14 at 15.11.31.png
Looks alright to me
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.
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