Maybe a bit messy, but pretty simple:
https://github.com/bhansconnect/roc-aoc-2023/blob/main/day2.roc
Definitely not speed optimal. Probably should do more reductions and less things that create extra temporary lists.
I'm at a friends wedding today, but had a few quick minutes to peek at the puzzle.
I thought I might leave a comment here and give the lukewilliamboswell/roc-parser a plug as it might save folks some time. :sweat_smile:
example using parser
Probably should put :point_up: in a spoiler tag
Mine is overly verbose but somewhat easy to follow I hope https://github.com/JRMurr/AdventOfCode2023/blob/main/roc/D02/Day.roc
some answer stuff
Here is mine: https://github.com/ostcar/aoc2023/blob/main/days/day02.roc
It was fun to work with the parser.
I feel like this was so much easier than day 1 :sweat_smile:
https://gitlab.com/arkandos/aoc/-/blob/2023/solutions/day2.roc
answer stuff
I felt today was alright with string splitting and backpassing. Was a bit afraid at first because I didn't know how to extract data from results in a nice manner, until I understood how to use Result.try
and backpassing. Though the repeated Result.try
at "every" line seems a bit annoying.
Part 1 and 2
Gotta say that no shadowing also bites me hard, though I guess that's a matter of unlearning what I'm used to.
Here's mine https://github.com/isaacvando/aoc/blob/main/2023/day2.roc
Gotta say that no shadowing also bites me hard, though I guess that's a matter of unlearning what I'm used to.
Yeah that has taken a fair amount of getting used to for me
Refactoring sorely needed, but I managed to solve it by extensive use of List.get
https://github.com/lindskogen/advent-of-code-2023/blob/main/day02/main.roc
my solution
Yeah, the lack of shadowing was made it harder for me too here. Currently shadowing also frequently causes a cascade of type errors as well, so it's often tough to see that the shadow is the issue. I know there are plans to fix that specific issue though.
I find that I won't shadowing a lot less often now that I have written a ton of roc, but when I do want it, the errors are just super annoying. And writing x1
, x2
, etc is super tedious and error prone especially with modifications.
Yeah. My sense is shadowing in the same scope is still probably not worth it, but I am pretty interested in allowing shadows across scopes now
I think allowing it across scopes but not in scopes would be super strange in roc due to backpassing:
invalid:
x = ...
x = ...
x = ...
allowed:
x <- ...
x <- ...
x <- ...
also this will probably happen then eventually :smiling_devil:
let = \val, fn -> fn val
x = 1
x <- x + 1 |> let
At least it is super explicit
First Roc-program ever. Took a while but I think I got the hang of it now :) Was a bit confused to not find filter
, map
and fold
/reduce
in the List
module but found their equivalents after reading a all the docs :)
https://github.com/kwando/AdventOfCode2023/blob/main/02/d2.roc
Looks nice!
Also, I am surprised that the compiler allows this without more indentation:
when color is
Red -> (value, g, b)
Green -> (r, value, b)
Blue -> (r, g, value)
while it's still top of mind, can we note the specific scenarios where shadowing would have been desirable in #ideas > Shadowing & Redeclaration for future reference?
Hannes Nevalainen said:
Was a bit confused to not find
filter
,map
andfold
/reduce
in theList
module but found their equivalents after reading a all the docs :)
nice! Did you find https://www.roc-lang.org/different-names or did you just figure them out on your own?
https://github.com/keeslinp/AoC2023/blob/main/day2.roc
I briefly went down the parser package route, but the doc just aren't quite there yet and I didn't want to have to dig through the source to figure out all out so I just did the boring "keep splitting until you have your values" strategy that works for most AoCs
What was really satisfying was that part two only took a few lines of code since I already had all the right data.
2 big shoutouts for roc on day 2:
Brendan Hansknecht said:
Also, I am surprised that the compiler allows this without more indentation:
when color is Red -> (value, g, b) Green -> (r, value, b) Blue -> (r, g, value)
Agreed it should probably be indented a one level more, this is was what the VSCode extension told me to do.
Also find it super convenient to have the expect
s right in the code. Just lovely :)
filed #6155
Richard Feldman said:
Hannes Nevalainen said:
Was a bit confused to not find
filter
,map
andfold
/reduce
in theList
module but found their equivalents after reading a all the docs :)nice! Did you find https://www.roc-lang.org/different-names or did you just figure them out on your own?
In my case, I've been finding them on their own. I've seen the "Tip: Some names differ from other languages.", but didn't expect it to be so helpful!
Richard Feldman said:
Hannes Nevalainen said:
Was a bit confused to not find
filter
,map
andfold
/reduce
in theList
module but found their equivalents after reading a all the docs :)nice! Did you find https://www.roc-lang.org/different-names or did you just figure them out on your own?
Nope, haven't seen that page :) I figured they had to live somewhere in the List
module so I just looked at all the ones the it could possibly be. I think walk
was the most confusing one since I associate that with a map
for some reason.
<random-feedback>I think the navigation in the docs page is not as good as it can be. The menu on the left has so much vertical padding that I found it hard to get a good overview and jump around to needed sections.</random-feedback>
My day 2
oof, took a while but I did it! Only part 1 for now as it's already pretty late. Took me the entire day to understand how to approach FP but it was so worth it :tada:
https://github.com/furtidev/aoc-2023/blob/main/day-02/main.roc
congrats, welcome to the party! :smiley:
Results & timing
Interestingly, for Day 2 using --optimize
is 10x faster for me. :rocket:
Timings that fast probably aren't super accurate.
That said, parser combinators probably are super slow without optimizations, so the giant gain makes sense
Yeah, you should probably use hyperfine or something like that, to get accurate results. This got me curious and I tested my solution and the difference between optimized and not is not that big: 1.2ms vs 1.6ms, respectively.
Here is mine.
https://gitlab.com/AsbjornOlling/aoc2023/-/blob/main/02/main.roc
Once I got my List Game
built (using roc-parser), it was a breeze.
Definitely a parser-heavy day.
It would probably have been faster for me to just do the .split().split()
thing, the format was simple enough that one could get away with it - but parser combinators are just way more fun.
I struggled a bunch with a memory bug in my expectations, but it looks like the bug is being looked at, so thats :sparkles:
EDIT: the bug has been fixed
Excited to do it again tomorrow.
John Murray said:
I feel like i always fall into the "trap" in AOC of spending half the time making a type that represents the data
But that part is the most fun!
Pearce Keesling said:
I briefly went down the parser package route, but the doc just aren't quite there yet and I didn't want to have to dig through the source to figure out all out ...
Is there anything in particular that would help you here or that you expected to see?
Like maybe a smaller example of how to write a parser like we see in these AoC solutions?
Yeah, I think an AOC style example is perfect. Now that I've seen yours I think I'm more likely to try it again tomorrow.
Honestly taking another look. I think just syntax highlighting would solve 90% of my problem. I was thinking "oh I just wish I had a list of combinators and what they do", but you basically already have that cause you've documented the code very well in comments. I think I just saw a big old wall of white text and didn't want to do the mental labor of parsing through it to find the ones I needed. Something like rustdoc would be suuuuper helpful here
LoipesMas said:
Yeah, you should probably use hyperfine or something like that, to get accurate results. This got me curious and I tested my solution and the difference between optimized and not is not that big: 1.2ms vs 1.6ms, respectively.
If timings are under about 5 to 10 ms, I wouldn't trust hyperfine. I would make your program internally repeat multiple times and measure that.
Luke Boswell said:
John Murray said:
I feel like i always fall into the "trap" in AOC of spending half the time making a type that represents the data
But that part is the most fun!
Lol yea I do agree with that. It's all about mindset. For this year I just wanna learn more roc so that approach is "right"
Parsers all the way today, but I definitely lost some feathers on the way :hot_face:
https://github.com/axelerator/aoc23/blob/main/02/main.roc
I am really enjoying reading these solutions to find out patterns and methods I did not think of. My solution is nothing fancy and could for sure be more terse, but I really enjoyed writing it! In general I feel like some kind of pattern of writing a solve
function that calculates based on a parse
function which in turn maps a parseLine
is nice, at least for these early solutions. Also the expect
s are super useful for finding bugs
Here is my day2:
Header
Nice, I think you can also do |> List.map .0
as shorthand for |> List.map \(val,_) -> val
if you want.
Ah nice! I saw above also that Str.splitFirst
would have been useful for me
My day 2 Did all the parsing with Str.split
and Str.splitFirst
. Wasn't too bad doing it that way this time.
Here's my solution for Day 2.
https://github.com/ryanb/advent-2023-roc/blob/main/day02/main.roc
I learned a lot reading the code here, especially regarding the parser. I'll have to try that out in the future.
Ryan Bates said:
Here's my solution for Day 2.
https://github.com/ryanb/advent-2023-roc/blob/main/day02/main.rocI learned a lot reading the code here, especially regarding the parser. I'll have to try that out in the future.
Parser combinators are magical and there are libraries in nearly every language these days.
Probably just me, but I have never liked parser combinators. Much prefer direct recursive descent parsers.
amusingly, I only knew parser combinations when I started Roc, which is why our parser is in that style even though it's not a good fit for Rust imo :big_smile:
Started to try to get an understanding of RoC by doing some AoC challenges. Lots of fun.
I made this construct:
Cubes :
{ red : U64
, green: U64
, blue : U64
}
Something is off with the body of the constraints definition:
38│ constraints : Cubes
39│> constraints = \_ ->
40│> { red : 12
41│> , green : 13
42│> , blue : 14
43│> }
The body is an anonymous function of type:
* -> {
blue : Num *,
green : Num *,
red : Num *,
}
But the type annotation on constraints says it should be:
{
blue : U64,
green : U64,
red : U64,
}
I started out with Nat instead of U64, but that was the same.
Coming from Elm, i kind of expect the compiler to figure this all out. What am i doing wrong?
You want either this
constants : Cubes
constants = {
blue : 12,
green : 13,
red : 14,
}
or
constants : {} -> Cubes
constants = \_ -> {
blue : 12,
green : 13,
red : 14,
}
I suspect you want the first version, but if you want constants
to be a function that waits to evaluate its body then you would define it the second way and call it as constants {}
{}
is an empty record, which is like the empty tuple type ()
in Elm
Oh. It is that simple. Thanks!
This type
* -> {
blue : Num *,
green : Num *,
red : Num *,
}
is the inferred type of
constants = \_ -> {
blue : 12,
green : 13,
red : 14,
}
what it's saying is
This is a function that takes a single argument that could be anything, and returns a struct of three number values. The numbers could be used in many different contexts, so we have not yet specialized to a concrete type.
You might find it helpful to mess with some number values in the REPL to get a better intuition for the inference behavior
» 12
12 : Num *
» 12.0
12 : Frac *
» 0.1 + 0.2
0.3 : Frac *
» 1 + 0.1
1.1 : Frac *
» x : U64
… x = 1
1 : U64
doing
constants : Cubes
constants = {
blue : 12,
green : 13,
red : 14,
}
is similar to
x : U64
x = 1
in that 12
/13
/14
are inferred generally as Num *
, but your type annotation asserts that in your use case you want the values to be encoded as U64
.
I run into something that i (as a beginner) really can't explain, which is that a value of a variable changes where i don't think i touched it.
dbg "\(acum) - value of acum at start of function: "
when characters is
[head, .. as tail] ->
dbg "\(acum) - value of acum in when clause"
dbg " value changed in some cases - how, why?"
So that variable acum
has a different value before entering and after entering the when
clause. How can that happen?
Here is a full program.
app "helloWorld"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.7.0/bkGby8jb0tmZYsy2hg1E_B2QrCgcSTxdUlHtETwm5m4.tar.br" }
imports [pf.Stdout]
provides [main] to pf
main =
outcome =
recurse testString None "" []
outcomeStr = Str.joinWith outcome "|"
dbg "Expected : |467|..|174|.."
dbg "But it is: \(outcomeStr)"
Stdout.line "Done - see dbg output"
TokenType : [
Digit,
Dot,
None,
]
testString =
Str.graphemes "467..174.."
tokenType : Str -> TokenType
tokenType = \str ->
when str is
"1" -> Digit
"4" -> Digit
"6" -> Digit
"7" -> Digit
"." -> Dot
_ -> None
# left a lot out for brevity
recurse : List Str, TokenType, Str, List Str -> List Str
recurse = \characters, previousTokenType, acum, strings ->
# idea is that this function makes a list of groups
# "467..174.." becomes [ "467, "..", "174", ".."]
# however, it becomes [ "667, "..", "774", ".."]
# odd things happen ^ ^
# acum is the buffer where characters are collected
# until the tokentype changes
# the value of acum somehow changes when entering
# the when clause in some cases - but how and why
dbg "-------"
dbg "\(acum) - value of acum at start of function: " # First
when characters is
[head, .. as tail] ->
dbg "\(acum) - value of acum in when clause"
dbg " value changed in some cases - how, why?" # Second
if (tokenType head) == previousTokenType
then
recurse
tail
previousTokenType
(Str.concat acum head)
strings
else
recurse
tail
(tokenType head)
head
(List.append strings acum)
[] ->
List.append strings acum
Definitely a bug on the roc side somehow
we must have a codegen issue leading to mutation or a refcounting issue of some sort.
Ok. Filed a bug at https://github.com/roc-lang/roc/issues/6239
I feel like talking to the past. :D
It is fun to solve the problem and then read all the cool solutions here. Afterwards, I want to improve and polish my solution with the new insights and patterns.
My solutions can be found here.
Some problems I encountered:
Default int type strikes again. I ran into some of this when working on Roc-Wasm4. When an int is captured, we often capture the wrong type. In this case it was captured as an i64, which is incorrect for the usage.
Brendan Hansknecht said:
When an int is captured, we often capture the wrong type.
wait, why do we often capture the wrong type? :sweat_smile:
I think it is a bug with default types. The closure confused the type generation. We make it the default type. Then when loading it from the capture we load it with the specific type
Happens with frac as well
interesting, we should try to find a minimal repro of that
I filed one last week #6387
Last updated: Jul 06 2025 at 12:14 UTC