Trying to find a simple way for people to solve AoC puzzles. Been thinking about different ways of doing things, and I think I like this. The idea being that I can make a package with a URL and then it's just a basic-cli app and the input comes from stdin
https://gist.github.com/lukewilliamboswell/3203c130855d4debc67d65265b5a172f
$ roc day1.roc < day-1-input.txt
--- ADVENT OF CODE 2023-1: Some Title ---
SOLUTION: The Elf with the highest calories has 62522 kCal
I can pretty up the output so it gives timing information, and displays colors in the terminal.
Or it's easy to use external tooling for samples/perf etc.
$ roc build --optimize day1.roc
$ ./day1 < day-1-input.txt
I'd been wondering if there should be an AOC platform, instead of being built on top of basic-cli. Like, you don't need all the I/O of basic-cli, you're pretty much just doing a calculation and printing it.
I guess there might be some situations where you want to save/read intermediate state in a file?
I've thought about that too.
I've been trying to think of something that is really similar to other ways of using roc, so it will be familiar to transition from say the tutorial to AoC, and then on to other things. So using basic-cli feels like a gentle gradient on that learning path.
My only real concern with making a new platform, is that it's another thing to maintain.
Yeah, I guess maintaining a platform is more work than maintaining a library.
One existential worry I have about Roc is that basic-cli will be the only platform, and all the effort put into the novel platform idea won't be used for much. AoC could be an opportunity to have another high-profile platform to add to the repertoire, so that people have a reason to learn and use multiple platforms.
I've made a package and published it, updated the example
https://gist.github.com/lukewilliamboswell/3203c130855d4debc67d65265b5a172f
Screenshot 2024-11-07 at 15.01.28.png
Lot's more to improve... but I'm going to try this out and see how it goes.
This is the new API
Solution err : {
year : U64,
day : U64,
title : Str,
part1 : Str -> Result Str err,
part2 : Str -> Result Str err,
} where err implements Inspect
I've updated my template repo https://github.com/lukewilliamboswell/aoc-template
I'm going to upgrade a few more of my solutions from previous years, maybe I'll tweak it and improve it some, before I make an announcement
As a former AoC learner, I would find it suspicious and less-satisfying to learn a programming language in a dedicated sandbox for that specific event. I'd ask "okay, but what does it feel like to ACTUALLY use Roc?" and hopefully push on to find out... but likely not.
If it's hard to use Roc's defaults for AoC, then that just means Roc is more complex than we'd like.
To someone unfamiliar with the importance of picking the right platform for your app, I expect that an AoC platform would seem more like helper macros or an alternative build of the language, which both feel like cheating.
19 messages were moved from this topic to #ideas > ASCII in builtins by Richard Feldman.
Kilian Vounckx said:
Maybe the template should use the roc-ascii package instead of Str? AoC is always ascii anyways, and it has some nice conveniences like case conversions
This is a great idea... I'll have a tinker and see what the experience is like.
@Kilian Vounckx @Hannes
I've had a brief tinker with roc-ascii... I changed the API to
Solution err : {
year : U64,
day : U64,
title : Str,
part1 : Ascii -> Result Str err,
part2 : Ascii -> Result Str err,
} where err implements Inspect
The issue I then had with this was that it was now difficult to do parsing using roc-parser which expects List U8 or Str. I probably need to think about how to get these to play nicely together.
One related philosophical question -- does Ascii need to be an opaque type? or is it sufficient just to be a wrapper around a List U8? It provides us compile time guarantees but also makes it harder to interface with.
I wonder what specific things people use ASCII for in AoC
for example, uppercase/lowercase could just be Str.withAsciiUppercased : Str -> Str
Or maybe?
Str.withAsciiUppercased : Str -> Result Str [InvalidAsciiCharacters]
I think the situation where someone wants to use this is where they're like "I know this is all ASCII because I control the input" (e.g. Advent of Code) and the Result would be more annoying than helpful
That could extend in future to
Str.withLocaleUppercased : Str, (Str -> Result Str [...]) -> Result Str [...]
Where you use a locale package to provide that implementation
hm, but is that more helpful than just using the package directly? :thinking:
separately - what other things do people use in advent of code?
besides uppercase/lowercase
I guess if there's a lot of working with individual ASCII characters, then List U8 is about as ergonomic as it's going to get without introducing another module :sweat_smile:
(or introducing footguns)
Personally, I have embraced the parser (which is 10x nicer now with record builder :heart:) so it's not really an issue for me.
yeah I'm just thinking about the beginner experience when someone doesn't already know how to use the parser
and are looking for something familiar, e.g. working with individual characters
I think telling people to use Str.toUtf8 and work with List U8 is ok for getting started with AoC.
Luke Boswell said:
One related philosophical question -- does
Asciineed to be an opaque type? or is it sufficient just to be a wrapper around aList U8? It provides us compile time guarantees but also makes it harder to interface with.
The reason I went with an opaque type for an ASCII Char is that a U8 can be from 0 to 255, but an ASCII character can only be from 0 to 127, so it's fairly easy to have a List U8 that is not valid ASCII. I definitely could make the ASCII string type just be an alias for List Char though, as I'm pretty sure any sequence of Chars is valid ASCII.
Here's the easy solution, just add a U7 type to the compiler! Then any List U7is guaranteed to be valid ASCII! :joy:
Str.toAscii : Str -> List U7
Would have to be Str -> Result (List U7) [InvalidAsciiChar] unfortunately :sweat_smile:
Last year, I used my own AoC-Platform, what was inside my AoC Repo. The main function was [Part1, Part2] -> Str and I imported the input-text with just with imports statement.
This year, I plan to do something similar. I will change the signature to use [Part1, Part2] -> List U8 to get rid of all the Utf8-Problems, that do not exist in AoC. I will probably also move the platform to its own repo, so it can be imported by any Roc file, making them stand alone scripts.
I get the point, that AoC should be a learning experience. And that using basic-cli with Tasks is more practical for many other projects. But I also think, that one of the basic ideas of Roc is, not to use a generic Platform/Framework for everything, but the best Platform/Framework for the specific problem. And I don't think, that basic-cli is the best basis for an AoC-problem.
I also don't think, that you have to use a Task for an AoC-problem. Roc got fantastic support for Tasks since the last AoC. But that does not mean, that you have to go for Tasks for every problem.
I like, that we can experience with different ways. basic-cli, a AoC-Module with module params or a individual platform. I am excited to see your solutions in December.
Str.withAsciiUppercased : Str ->
This would be a footgun. People would use it when they think they only have ASCII but don't (concrete example). So it should be in an ascii library, not Str.
It sounds like that factorio guy hand rolled his own unicode normalisation. I hope it's not that hard to do it right. :sweat_smile:
What about the "echo platform" proposed in the purity inference doc? It had a section describing how a new user could just have a file with contents of echo! "hello world!" and roc could run it, without any app headers. I understand this is quiet an undertaking, since the time is short. If it could be done in time, we could suggest for AoC to don't even bother with the header, just get coding! that platform (which I've named echo platform, since all it can do is echo to stdout) would be baked into the compiler (no BLAKE3 hash in header, extremely small binaries to show what's possible).
That being said, I did AoC with basic-cli and it was perfectly fine. Except at the time, the binaries were 100+Mb, which was concerning.
Local helloworld using basic-cli is now 6.7 Mb
Hannes said:
Here's the easy solution, just add a
U7type to the compiler! Then anyList U7is guaranteed to be valid ASCII! :joy:
Well, look what I just now accidentally found in the wild! :laughing: Makes sense, though.
https://docs.rs/midly/latest/midly/enum.MidiMessage.html
Is it boxing individual u7s?
Hehe I haven't seen it do any boxing yet, but the "What's the in box?!?" Se7en movie quote reference was too tempting, since you could call the dropped bit the head.
Last updated: Jun 16 2026 at 16:19 UTC