Just finished up day 1 and day 2. Some random thoughts so far:
Looking forward to continuing with it for a while this month. Hopefully I can help provide some feedback along the way.
Most importantly, thank you for building something cool and sharing it with the world! I know it takes a lot of time, effort, and caring to work on all this.
Roc on! :tada: :rock_on:
The hardest part of actually using it for me so far has been not being able to see the inferred types of things I've written. I'm used to hovering for types in my IDE or being able to load the module in the repl and explore that way.
This is also the biggest pain point for me. It will be alleviated by the editor, but the editor hasn't been a priority for development recently, due to a variety of other things that have come up. I've written a small lsp server that queries the compiler for type information to make hover easier in the meantime, but to say it works well is an overstatement :sweat_smile:
I find myself missing tuples a lot. Not sure if that will get better or worse as I use it more.
Tuples are planned to be supported, and should land relatively soon!
being able to assign variables while destructuring into tuples, tags, or lists could help
could you elaborate what you mean by this? does this go beyond destructuring assignments you can do in e.g. pattern matching?
We technically already have a way to make tuples They just are witten weird. You have to use a tag. T var1 var2 var3
or Pair a b
you can pick any name you want as long as it starts with a capital letter. Then you can just destructure it to get the valued back out.
at least this way we can finally agree on pronunciation, as Tooooople a b
is different than Tuhpul a b
.
I think having pattern matching would be great, but specifically for the string splitting case, I can typically split and assign to variables in a list or tuple at once.
# elixir
[cmd, val] = String.split(line)
-- haskell
let (cmd, val) = break (== ' ') line
// js
const [cmd, val] = line.split(' ')
# roc
lineToDir = \line ->
result =
parts = Str.split line " "
cmd <- List.get parts 0 |> Result.try
val <- List.get parts 1 |> Result.try
Ok (Tuple cmd val)
Result.mapErr result \_ -> InvalidLine line
I can see how this is more a conversation about safety than "tuples", but list/tuple destructuring is very common in most FP. Whether I just need to shift my mindset to using records more is one thing, but in this instance, it's obvious that there's a step in there no matter what because the Str.split
returns a List
which can't be destructured.
For platforms, there are some talks linked on roc-lang.org. Those may help. If not, the main idea is just have a shell that manages all effects around a purely functional core. A platform is kinda like a runtime or framework. It enable roc to be a pure functional language while doing stuff. Of course there are other ways to create a similar abstraction, but generally that requires implementing all possible effects in the standard library. This also, hopefully, makes roc great for being a scripting language. For example, it could be used instead of Lua in a game.
Try Str.splitFirst
instead of Str.split
if you want cleaner matching. But yeah, we don't have any list pattern matching yet and that is quite inconvenient.
Even once we have list pattern mstching, i think it will still be more verbose due to needing to support error cases. Probably would require a when clause with a default case. Tuples, on the other hand, can just be pattern matched/destructured directly.
I think I understand that aspect of platforms. It does seem like a cool idea. The part I'm interested in as a new Roc dev getting up and running is "as someone who's trying to write a program in Roc, what platform do I pick?" and "what if I don't know how to code a platform but want to use Roc?" It seems like the hopeful answer to this is that there will be many choices and that people will be able to recommend a good base platform.
I think it might be outside the wheelhouse for a lot of users to discern between good platforms. Given the low level nature of platform code, I'd like a well-vetted platform that I can be reasonably sure doesn't contain horrible memory unsafe bugs and security holes. Having many community platforms seems to divide that attention and possibly allow bugs to exist longer without detection.
I also wonder about what platforms will support. It seems like one well-written platform could become the default for so many people that it keeps growing features until it just becomes a "standard runtime" that a different language would have.
Thanks for the rec on splitFirst
.
Expanding on the destructuring and pattern matching, this example comes from AoC 2021 day 2. Here's some haskell code that does this in a slightly less verbose way (whether that's good or not).
parseDir :: String -> (String, Int)
parseDir line = do
let (cmd, valStr) = break (== ' ') line
(cmd, read valStr :: Int)
move :: (Int, Int, Int) -> (String, Int) -> (Int, Int, Int)
move (h, d, a) ("up", val) = (h, d, a - val)
move (h, d, a) ("down", val) = (h, d, a + val)
move (h, d, a) ("forward", val) = (h + val, d + val * a, a)
move (h, d, a) _ = (h, d, a)
solve :: [(String, Int)] -> (Int, Int)
solve dirs = do
let (horiz, depth, aim) = foldl move (0, 0, 0) dirs
(horiz * aim, horiz * depth)
main :: IO ()
main = do
input <- lines <$> readFile "input.txt"
print $ solve $ map parseDir input
The interesting thing is that break
will return ([], [])
for empty strings. We can then pass it forward to move
and handle a malformed command as a no-op. I could see a real production system that would want to be able to ignore malformed inputs instead of error handling there.
The other piece of this that I mentioned in the initial post was the option to pick erroring code. I did this here with the read valStr :: Int
. I'm not trying to advocate for one side or the other though. I'm sure there's value to having none of this in a language. But it does help in some cases and it certainly helps with things that can crash and that's OK (e.g. a personal project that will only ever run on my machine).
Your idea around platforms is pretty accurate. Though I don't expect a single standard platform to emerge. I think what is much more likely is that a standard platform or 2 will emerge per category. This is the standard web server platform, standard cli app platform, standard permission restricted cli app platform, standard 2d game platform, etc.
The average Roc user should only have to search a platform repository, read some reviews (maybe sort by stars or something), and then pick a platform. They would be able to look at the fulls docs for its api, and use it by pointing their app at the url. They would not have to understand how it works under the hood.
break will return ([], []) for empty strings
We could propose an api change instead of returning a result for Str.splitFirst
. Or maybe add a builtin like that if we want to avoid the failure case. Not sure the pros/cons. If you want, make a thread in #ideas
Though also, you could just Str.splitFirst someStr " " |> Result.withDefault {before: "", after: ""}
more verbose, but much more concise than pattern matching
Yeah that splitFirst
Result.withDefault
kind of approach was kind of what I was alluding to about maybe just getting more in the record mindset. I'm happy to change my mindset for the opinions of the language because I think opinionated languages remove a lot of discussion/uncertainty noise for developers. I'll hold off on posting in #ideas until I've used it more and can form a better opinion.
I am very pleasantly surprised that this:
something <-
value
|> thingThatReturnsResult
|> convertResultToTask
|> await
# ...
correctly desugars. this is amazing for clarity
Multi-line backpassing with pipelining....awesome. i never realized that worked.
yeah my gut feeling was that it wouldn't, but while refactoring I just tried it on a whim
because at least from my understanding that actually needs some special handling (since the function from the backpass needs to be passed to the last call in the pipeline, not the result of the pipeline)
Tagged unions which can have aliased names are so nice. being able to have Up
and Up Nat
allows for a type of matching that i'm often missing in elm. of course, i'll have to see if that becomes a maintainability problem in larger applications... but since the compiler can fully resolve all the types i don't see this being more than a momentary confusion.
@Gabriel Pickl curious about this last comment. Could you give an example?
Last updated: Jul 06 2025 at 12:14 UTC