Python has PEP 8 which defines a style guide for Python. Does the same thing exist for Roc? If not, I think it would be very valuable.
For example, I used the following style in one of the exercism exercises:
result = [1, 3, 4, 6, 8, 9, 11]->find(6)
But @Anton pointed out that most people would find the following style more familiar:
result = find([1, 3, 4, 6, 8, 9, 11], 6)
I personally prefer the first style in this case, as I find it more readable, but I'm happy to change it to the more familiar style if that's more idiomatic in Roc. That's where a style guide would help: when people don't have a strong preference, it would help make Roc code more homogeneous and reduce aesthetic debates.
I feel like there is a lot of exploration we need to do before we could say this or that style is more idiomatic.
With this specific example I feel like Anton is probably thinking about strangeness budget... his suggestion looks totally normal in almost every language.
However I think I would write this the first way because I have been indoctrinated somewhat and like our new ability to chain things together ala the old pipelines -- which is probably a more historically "functional" style.
Roc has some cool iterative looking things that are totally functional under the hood. It's a new world.
How should we present Roc to people?.. I think I lean towards presenting Roc as friendly and familiar as possible, and if these other patterns emerge as nicer after we have more experience using Roc then we will naturally begin to see more of that. The reason for this is to reduce the number of variables or things to think about for people unfamiliar with Roc.
That's totally reasonable, I'll switch to the more classical style then. :+1:
I've been keeping some notes in a gist ... it's starting to become really helpful as I migrate things
https://gist.githubusercontent.com/lukewilliamboswell/241a4e8adb7c89c7e0e02f1d303a8fa1/raw/style.md
If anyone has feedback let me know
Awesome thanks! Can you help me understand the difference between using the explicit wrapper body or not?
to_inspect : Markdown -> Str
to_inspect = |node| inspect_markdown(node)
# versus
to_inspect = inspect_markdown
Is it to make the argument name node more clear? Or is there a difference in how it’s compiled?
there was a bug in the old compiler which meant you had to do it that way, but in the new compiler, I thought we fixed it and to_inspect = inspect_markdown works now :thinking:
does it error out or something? (If it works, stylistically I'd do the more concise way)
I think I noticed that a couple weeks ago messing with the wasm4 stuff and then noticed it got subsequently fixed, will give it another try when I get the chance
actually I'd wait on wasm4 - I have a big change in the works for that!
Oh sounds cool!! Yeah I was just poking around cause I never thought what a pure functional game loop would look like, I was thinking about starting a discussion on different architecture approaches there. That’s also why I was getting interested in pure Random APIs because using effectful rng seemed to cause exclamation points to kind of proliferate through the code. Curious about ergonomic tradeoffs, do you want to just play a sound effect right now, or is it worth passing a bunch of events around to keep purity and play the sound later etc. Also was realizing that you could probably write imperative roc if you wanted to lol, just use effects wherever, use var extensively, but I guess that type of thing makes Roc easy to use as a quick scripting language when you do want that. But yeah looking forward to see what youre working on!
@Austin Clements in case you haven't seen it https://github.com/kili-ilo/roc-random
I think the general approach I would recommend is use the platform to get a random seed, and then use a pseudo random generator.
Also -- the wasm4 API is super effectful. If you compare that to say roc-signals where there is zero effectful code and it's all pure.
var is completely immutable under the hood -- it's just an imperative style or sugar on top. It's zero cost to write that way.
You didn't ask ... but I just love this example so much I want to share it again. :grinning_face_with_smiling_eyes:
Using record builders can make it super nice for generators. You say - give me a list of 1_000 random Character values and it just works.
Character : {
name : Str,
strength : U8,
agility : U8,
hit_points : U8,
lucky : Bool,
}
character_generator : Str -> Random.Generator(Character)
character_generator = |name| {
d20 = Random.bounded_u8(1, 20)
lucky_generator = Random.map(Random.bounded_u8(1, 20), |roll| roll == 20)
{
name: Random.static(name),
strength: d20,
agility: d20,
hit_points: Random.bounded_u8(8, 16),
lucky: lucky_generator,
}.Random
}
Hi Luke, big fan of your work! Yeah that makes total sense about the var thing. And the record builder generator is super cool! With some of the wasm4 examples I was just seeing what it would look like to make the main loop work like: effectful get input, pure state update, effectful render, something like that. Was just thinking about pros and cons, because it's nice that the game state update becomes pure but it does incur a cost of needing to pass information between the different stages of the game loop I guess, and maybe scatters the game behavior to different places in source etc
Here's something that does relate to code style though: with the random API I thought it might be more concise for generators to return a tuple of (value, Random.State) rather than return a record, maybe just a preference thing. I saw in the style guide you had a good example where using a tuple (Str, Str) could cause confusion, versus using a record where the fields have meaningful names. I was thinking in this case with the random api the type system would probably keep people from accidently using the tuple items in the wrong order though
Yeah, it's something I've been trying out in different places. In general I like using records more for API's -- and I feel like tuples are nicer in more temporary situations like scaffolding something for a match or intermediate map or fold etc. I wouldn't say I have a good vibe or strong opinion on it yet though.
in the future I want to have a special thing for tuples like this - I have a vague design doc for it
the basic idea is that instead of:
(num1, $seed) = $seed.step(generator)
(num2, $seed) = $seed.step(generator)
...you can do:
num1 = $seed<-step(generator)
num2 = $seed<-step(generator)
and the second thing is just syntax sugar for the first one
so $foo<-method(...) means:
$foo.method(...)$foo$foo to that valueit's useful for random seeds and also at least one other use case I have partially designed but not totally fleshed out yet: simulation tests, where you're writing a pure function that simulates all your I/O (and there's a simulation state you want to step through in a similar way to random number seeds)
Oh nice that's awesome, I guess useful for anything where you have to thread state through a series of operations? Trying to think of other use cases, would a hasher be one? Though I guess a hasher doesn't have any other return value besides it's next state so maybe $hasher = $hasher.hash(value) works fine already (sorry I forget the actual API for it lol)
I guess another use that comes to mind could be parsing or deserialization? Not too familiar but seems like there’s already some other machinery to help with that maybe
What would be the non syntax sugar equivalent of this?
(num1, num2) = $seed<-step(generator)
Would it be this?
(num1, num2, $seed) = $seed.step(generator)
Or that?
((num1, num2), $seed) = $seed.step(generator)
the last one
Last updated: Jul 23 2026 at 13:15 UTC