:partying_face: https://gitlab.com/JanCVanB/aoc-2024/-/blob/main/day-03/part-1/main.roc
Oh how i long for a Str.walkUntil
:partying_face: https://gitlab.com/JanCVanB/aoc-2024/-/blob/main/day-03/part-2/main.roc
I've got a nice solution... but I'm getting a fun one of these if I use my template :sad:
thread '<unnamed>' panicked at crates/compiler/mono/src/ir.rs:6166:56:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Oh found it...
part1 : Str -> Result Str _
part1 : Str -> Result Str []
Here is mine https://github.com/ostcar/aoc2024/blob/main/day03.roc
Ok, so here's my solution
https://github.com/lukewilliamboswell/aoc/blob/main/2024/03.roc
TIMING:
READING INPUT: <1ms
SOLVING PART 1: 1ms
SOLVING PART 2: 1ms
@Luke Boswell have a look at #bugs > Compiler crash when it can not find out the Err type
Another workaround the error is, if you append a |> Result.mapErr \_ -> PleaseFixMe
to your function
Ah nice, I'll fix the template so others wont run into that. Just tested and it fixes the problem. Just made it this instead
Solution err : {
year : U64,
day : U64,
title : Str,
part1 : Str -> Result Str [Err]err,
part2 : Str -> Result Str [Err]err,
} where err implements Inspect
Luke Boswell said:
Ok, so here's my solution
Ooh...
Oskar Hahn said:
Here is mine
Ooh...
:laughing: I forgot List.sum
exists - no regrets on List.walk 0 Num.add
:sunglasses:
I couldn't resist - @Oskar Hahn I inverted my split like yours, to drop 8 lines of code :partying_face:
RE: Spoiler
By the way: I don't think that splitting is a good solution. But if the solution works in a handful of milliseconds, there is no need to optimize it :smiley:
There are so many metrics of goodness, and performance is only one!
IMO, in most projects, the simplest solution furthers your high-level goals more than a high-performance one could.
However, beauty/satisfaction is another metric, and that's always subjective (and sometimes aligned with high performance).
Another day another solution: https://github.com/faldor20/aoc-template/blob/c398af5fde1169b8609ef4bd6b9465de0f380ab3/examples/2024/03.roc
I was keen to try out @Luke Boswell's parser. It took me a few iterations to get the right format. But I'm happy with the result.
The performance on the other hand.....well look.... performance isn't everything right :sweat_smile:
reading took: 0.039441ms
part1 took: 167.797391ms
part2 took: 168.628369ms
I kind of want to try hand rolling a parser to find out why it's so crazily slow
well.... a run of perf wasn't exactly revealing :sweat_smile:
image.png
@Brendan Hansknecht is the advice for perf still to run something like this?:
roc build --optimize --profiling examples/2024/03.roc
perf record -F999 --call-graph dwarf -- ./examples/2024/03
Give samply a try maybe?
Splitting strings took me longer than everything else combined:
https://github.com/r-bar/advent24/blob/master/day03/p2.roc
Now to go see how everyone else got done so fast.
I am running my own simpler template, so I don't get fancy performance numbers out of the box.
❯ roc build --optimize p2.roc
0 errors and 0 warnings found in 800 ms
while successfully building:
p2
❯ poop './p2 input.txt'
Benchmark 1 (898 runs): ./p2 input.txt
measurement mean ± σ min … max outliers
wall_time 5.50ms ± 339us 5.11ms … 6.56ms 0 ( 0%)
peak_rss 839KB ± 14.3KB 627KB … 856KB 6 ( 1%)
cpu_cycles 22.0M ± 291K 21.8M … 25.5M 84 ( 9%)
instructions 99.3M ± 76.5 99.3M … 99.3M 20 ( 2%)
cache_references 39.0K ± 62.8K 23.0K … 980K 133 (15%)
cache_misses 3.37K ± 603 2.56K … 6.39K 24 ( 3%)
branch_misses 36.8K ± 1.26K 35.7K … 45.4K 59 ( 7%)
sorry wtf is the poop command ?? :joy:
Luke Boswell said:
Give samply a try maybe?
I've not seen samply, that's cool!
I realized I needed to disable the surgical linker! it's working with both perf and samply now.
https://github.com/andrewrk/poop
Nothing particularly interersting really. but it's clearly spending its time inside the parser.
image.png
okay, so basically all it's doing is concatenating strings:
image.png
AHA! I understand what's happening! @Luke Boswell
you report your fancy errors when a parser fails, but my parser is failing almost 3 times for every single input :sweat_smile:
Because It's trying inputs sequentially.
It'd be interesting to explore a version of the parser library that handles failure quickly. We could potentially thread a config object through the parser that enables verbose error logging or fast error logging.
That's what rust's nom
seems to do and I understand it to be very very fast
okay @Luke Boswell I removed every instance of string interpolation in your passing lib:
reading took: 0.032032ms
part1 took: 1.232324ms
part2 took: 1.291891ms⏎
Not a bad 120x speed up :sweat_smile:
https://www.google.com/search?client=firefox-b-d&q=nom+error+handling Is an interesting read on this
Leaned to use parser in Roc with this one! loved it
https://github.com/thenikso/advent-of-code-2024-roc/blob/main/03.roc
I also leaned heavily on @Luke Boswell and his excellent parser. Congratulations on the documentation, whose copious use of examples really suits my horrible way of learning things. For Part 2 I just split on do()
and don't()
, crossing my fingers that they would follow in a regular pattern ... and found that they did. I felt a bit guilty not doing Part 2 from first to last as a parser -- but the splitting strategy actually gets things done faster, no doubt because my parser is not efficiently constructed and part 2 is therefore dealing with a much shorter string to parse.
I just implemented a simple parser, walking the chars through a state machine. Easy peasy:
https://github.com/gamebox/aoc-2024/blob/main/day3/puzzle2/main.roc
I could probably get it faster if I removed all of the string work and added a case for each piece of each op
Interesting, while doing that I got a very strange compiler crash:
thread '<unnamed>' panicked at crates/compiler/mono/src/ir/decision_tree.rs:777:17:
IntLiteral([44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], U8)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
During mono?
Updated the above with the current code. Have to head out for work...let me know if anyone has any ideas
Looks like the compiler is looking for an Identifier or a an Underscore pattern, but it's getting an IntLiteral
This is the relevant code:
debug_assert!(
matches!(pattern, Pattern::Identifier(_) | Pattern::Underscore,),
"{pattern:?}",
);
thread '<unnamed>' panicked at crates/compiler/mono/src/ir/decision_tree.rs:777:17: IntLiteral([44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], U8) note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
I could not find any existing issue about this, can you file a new one?
I'll have to figure out a minimal repro
I can try to do that tonight
Damn AOC really bites into my limited contribution time
I'll have to figure out a minimal repro
Feel free to toss in the entire thing if you're short on time :)
Ok
Created: https://github.com/roc-lang/roc/issues/7302
Updated this with a lot of debugging findings
Eli Dowling said:
okay Luke Boswell I removed every instance of string interpolation in your passing lib:
reading took: 0.032032ms part1 took: 1.232324ms part2 took: 1.291891ms⏎
Not a bad 120x speed up :sweat_smile:
@Eli Dowling do you have anything more on this I could see? I'm interested to know if there is anything here we can roll into the parser for others to benefit from
Haven't seen Eli's solution, but that's the exact thing I was trying to do when my compilation blew up. Hoping to see similar improvements :-)
Eli Dowling said:
well.... a run of perf wasn't exactly revealing :sweat_smile:
image.pngBrendan Hansknecht is the advice for perf still to run something like this?:
roc build --optimize --profiling examples/2024/03.roc perf record -F999 --call-graph dwarf -- ./examples/2024/03
I'm late to the party. Yes with the caveat that on Linux you want --linker=legacy
and it works best of the platform is built with debug info.
Luke Boswell said:
Eli Dowling said:
okay Luke Boswell I removed every instance of string interpolation in your passing lib:
reading took: 0.032032ms part1 took: 1.232324ms part2 took: 1.291891ms⏎
Not a bad 120x speed up :sweat_smile:
Eli Dowling do you have anything more on this I could see? I'm interested to know if there is anything here we can roll into the parser for others to benefit from
I'll look into it, but currently I made it faster by basically removing all error logging... Not something you want.
I'd say we need to thread a parser config through, and make the logs a tag union, either just a tag indicating the current node that failed which is what you do normally, it's full string log if we have the debug config setting.
Definitely take a look at the link to nom I shared where they talk about efficient error handling :)
Another thing we could do is have a parser function that specifically allows attaching errors to the parser. So if it gets an error it adds in a message, that wya the user can give their own parser errors! Angstrom in ocaml does this, and I like it :)
Here's my solution, I thought this one was fun https://github.com/isaacvando/aoc/blob/main/2024/3.roc
Eli Dowling said:
Another thing we could do is have a parser function that specifically allows attaching errors to the parser. So if it gets an error it adds in a message, that wya the user can give their own parser errors! Angstrom in ocaml does this, and I like it :)
This is also what’s done in FParsec which is what I used for the parser for my language. I was about to explore it more once I started to rewrite the parser - but that’s on hold for now, maybe indefinitely.
Anthony Bullard said:
I finally fixed this through some convoluted means of moving all literal matching out into the guards. I don't know if that's why, but the solution is about the same as the old one. So a terrific waste of time learning moment. :smile:
It's a little under the wire, but here's my stab at it: https://github.com/Saalico/aoc_2024/blob/main/day3/main.roc
I'm very much a newb at coding, especially roc, so open to suggestions/call outs if any of you are so inclined. Happy coding!
Already one day late in the advent of code :smile: https://github.com/RobinCamarasa/Advent-of-code/blob/master/2024/day03/RobinCamarasa/main.roc
Jesse Nutt said:
It's a little under the wire, but here's my stab at it: https://github.com/Saalico/aoc_2024/blob/main/day3/main.roc
I'm very much a newb at coding, especially roc, so open to suggestions/call outs if any of you are so inclined. Happy coding!
I like your solution witch looks very concise but you're lucky that the input didn't test your failures :sweat_smile: (I've seen the same issue in several other solutions)
Your response to mul(2,4)3,5)
might be 23
but the good one is 8
.
Ghislain said:
Jesse Nutt said:
It's a little under the wire, but here's my stab at it: https://github.com/Saalico/aoc_2024/blob/main/day3/main.roc
I'm very much a newb at coding, especially roc, so open to suggestions/call outs if any of you are so inclined. Happy coding!
I like your solution witch looks very concise but you're lucky that the input didn't test your failures :sweat_smile: (I've seen the same issue in several other solutions)
Your response to
mul(2,4)3,5)
might be23
but the good one is8
.
Ack! That's embarrassing! Is there really such a case that other puzzle inputs would have thrown that edge case?
Either way, let's take another swing:
part1 = \in ->
Str.splitOn in ")"
|> List.joinMap \x ->
if Str.contains x "mul(" then
Str.splitOn x "mul("
else
[""]
|> List.keepIf \chunk -> (List.all (Str.toUtf8 chunk) \c -> List.contains closeToken c) && !(Str.isEmpty chunk)
|> List.keepOks \x -> mulString x
|> List.sum
Hmm, why don't we use a regex here? (Because roc does not yet support it properly?)
Yes, we don't have a regex package yet
The first one solved by the Roc Claude agent:
main_claude.roc
prompt-aoc-3-part1.txt
prompt-aoc-3-part2.txt
I did have to fiddle with the prompt a bit; it was using some old Roc syntax, I told it to use intermediary variables with expect
and had to show it how to properly use dbg
.
Hey, one thing that would be cool is actually recording you doing one of these end-to-end.
uhu, I might do a simple screen recording later :)
I am a bit slow, christmas holidays only started now, so i hope to have a little more time to spend on these puzzles.
I try to solve #3 with a parser, because, i like to understand parsers. I am not yet grasping the two type
mulParser : Parser String.Utf8 Mul
mulParser =
const (\first -> \second -> { first: first, second: second })
|> skip (string "mul(")
|> keep digits
|> skip (string ",")
|> keep digits
|> skip (string ")")
mParser : Parser String.Utf8 Mul
mParser =
const identity
|> skip (chompUntil "mul(")
|> keep mulParser
So now i get a compiler issue stating:
── TYPE MISMATCH in src/S2024/D03.roc ──────────────────────────────────────────
Something is off with the body of the mParser definition:
64│ mParser : Parser String.Utf8 Mul
65│ mParser =
66│> const identity #
67│> |> skip (chompUntil "mul(")
68│> |> keep mulParser
This Parser.keep call produces:
Parser (List Str) Mul
But the type annotation on mParser says it should be:
Parser (List U8) Mul
Which leaves the question, how do i map a Parser (List Str) Mul
to a Parser (List U8) Mul
, or more general, a how to map a Parser a x
to a Parser b x
?
@Luke Boswell
I'm not very good at parser, but I think the fundamental issue is that chompUntil
is meant to work on single characters, not full strings.
By saying chompUntil "mul("
, you are requesting that the input be a List Str
like ["test", "mul()", "other", "something", "mul(", "last"]
. Then it would consume strings looking for exact matches until it ends up only having the list ["mul(", "last"]
. I don't think this is the behaviour you want.
My gut feeling is that you use something like alt
to attempt to parse a mulParser
. If that fails, you would fall back on a parser that just consumes a single character.
Then loop that
but there is probably a smarter way. I'm really not a fan of parser combinators most of the time so I don't really know how to use them well.
Last updated: Jul 06 2025 at 12:14 UTC