Stream: advent of code

Topic: 2023 Day 2


view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 05:40):

Maybe a bit messy, but pretty simple:
https://github.com/bhansconnect/roc-aoc-2023/blob/main/day2.roc

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 05:41):

Definitely not speed optimal. Probably should do more reductions and less things that create extra temporary lists.

view this post on Zulip Luke Boswell (Dec 02 2023 at 06:06):

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

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 06:11):

Probably should put :point_up: in a spoiler tag

view this post on Zulip John Murray (Dec 02 2023 at 06:19):

Mine is overly verbose but somewhat easy to follow I hope https://github.com/JRMurr/AdventOfCode2023/blob/main/roc/D02/Day.roc

view this post on Zulip John Murray (Dec 02 2023 at 06:21):

some answer stuff

view this post on Zulip Oskar Hahn (Dec 02 2023 at 07:38):

Here is mine: https://github.com/ostcar/aoc2023/blob/main/days/day02.roc

It was fun to work with the parser.

view this post on Zulip joshi (Dec 02 2023 at 11:41):

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

view this post on Zulip Jean Niklas L'orange (Dec 02 2023 at 12:42):

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

view this post on Zulip Jean Niklas L'orange (Dec 02 2023 at 12:44):

Gotta say that no shadowing also bites me hard, though I guess that's a matter of unlearning what I'm used to.

view this post on Zulip Isaac Van Doren (Dec 02 2023 at 15:23):

Here's mine https://github.com/isaacvando/aoc/blob/main/2023/day2.roc

view this post on Zulip Isaac Van Doren (Dec 02 2023 at 15:23):

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

view this post on Zulip Johan Lindskogen (Dec 02 2023 at 15:30):

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

view this post on Zulip Ayaz Hafiz (Dec 02 2023 at 16:26):

my solution

view this post on Zulip Ayaz Hafiz (Dec 02 2023 at 16:28):

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.

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 16:38):

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.

view this post on Zulip Ayaz Hafiz (Dec 02 2023 at 17:10):

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

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 17:12):

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 <- ...

view this post on Zulip joshi (Dec 02 2023 at 17:16):

also this will probably happen then eventually :smiling_devil:

let = \val, fn -> fn val
x = 1
x <- x + 1 |> let

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 17:17):

At least it is super explicit

view this post on Zulip Hannes Nevalainen (Dec 02 2023 at 18:12):

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

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 18:14):

Looks nice!

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 18:14):

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)

view this post on Zulip Richard Feldman (Dec 02 2023 at 18:19):

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?

view this post on Zulip Richard Feldman (Dec 02 2023 at 18:20):

Hannes Nevalainen said:

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 :)

nice! Did you find https://www.roc-lang.org/different-names or did you just figure them out on your own?

view this post on Zulip Pearce Keesling (Dec 02 2023 at 18:21):

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:

  1. piping all the things is super satisfying, being able to define my top-level parts are just two long pipes is very elegant
  2. I had to write out exactly 0 types because it was able to properly infer everything even though I was doing lots of tags and records

view this post on Zulip Hannes Nevalainen (Dec 02 2023 at 18:39):

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 expects right in the code. Just lovely :)

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 18:44):

filed #6155

view this post on Zulip Jason Hobbs (Dec 02 2023 at 18:54):

Richard Feldman said:

Hannes Nevalainen said:

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 :)

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!

view this post on Zulip Hannes Nevalainen (Dec 02 2023 at 18:56):

Richard Feldman said:

Hannes Nevalainen said:

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 :)

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>

view this post on Zulip Jason Hobbs (Dec 02 2023 at 19:01):

My day 2

view this post on Zulip [REDACTED] (Dec 02 2023 at 20:38):

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

view this post on Zulip Richard Feldman (Dec 02 2023 at 20:54):

congrats, welcome to the party! :smiley:

view this post on Zulip Luke Boswell (Dec 02 2023 at 21:57):

My solution for Day 2.

Results & timing

view this post on Zulip Luke Boswell (Dec 02 2023 at 22:10):

Interestingly, for Day 2 using --optimize is 10x faster for me. :rocket:

view this post on Zulip Brendan Hansknecht (Dec 02 2023 at 22:20):

Timings that fast probably aren't super accurate.

That said, parser combinators probably are super slow without optimizations, so the giant gain makes sense

view this post on Zulip LoipesMas (Dec 02 2023 at 22:28):

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.

view this post on Zulip Asbjørn Olling (Dec 02 2023 at 22:31):

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.

view this post on Zulip Luke Boswell (Dec 02 2023 at 22:57):

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!

view this post on Zulip Luke Boswell (Dec 02 2023 at 22:59):

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?

view this post on Zulip Pearce Keesling (Dec 02 2023 at 23:37):

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.

view this post on Zulip Pearce Keesling (Dec 02 2023 at 23:40):

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

view this post on Zulip Brendan Hansknecht (Dec 03 2023 at 00:04):

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.

view this post on Zulip John Murray (Dec 03 2023 at 03:01):

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"

view this post on Zulip Axel (Dec 03 2023 at 03:09):

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

view this post on Zulip Johan Lövgren (Dec 03 2023 at 10:03):

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 expects are super useful for finding bugs
Here is my day2:

Header

view this post on Zulip Luke Boswell (Dec 03 2023 at 10:21):

Nice, I think you can also do |> List.map .0 as shorthand for |> List.map \(val,_) -> val if you want.

view this post on Zulip Johan Lövgren (Dec 03 2023 at 10:48):

Ah nice! I saw above also that Str.splitFirst would have been useful for me

view this post on Zulip timotree (Dec 03 2023 at 23:34):

My day 2 Did all the parsing with Str.split and Str.splitFirst. Wasn't too bad doing it that way this time.

view this post on Zulip Ryan Bates (Dec 04 2023 at 17:55):

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.

view this post on Zulip Axel (Dec 05 2023 at 00:06):

Ryan Bates said:

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.

Parser combinators are magical and there are libraries in nearly every language these days.

view this post on Zulip Brendan Hansknecht (Dec 05 2023 at 00:15):

Probably just me, but I have never liked parser combinators. Much prefer direct recursive descent parsers.

view this post on Zulip Richard Feldman (Dec 05 2023 at 04:38):

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:

view this post on Zulip Eelco Hoekema (Dec 05 2023 at 16:05):

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?

view this post on Zulip Elias Mulhall (Dec 05 2023 at 16:08):

You want either this

constants : Cubes
constants = {
    blue : 12,
    green : 13,
    red : 14,
}

or

constants : {} -> Cubes
constants = \_ -> {
    blue : 12,
    green : 13,
    red : 14,
}

view this post on Zulip Elias Mulhall (Dec 05 2023 at 16:09):

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 {}

view this post on Zulip Elias Mulhall (Dec 05 2023 at 16:09):

{} is an empty record, which is like the empty tuple type () in Elm

view this post on Zulip Eelco Hoekema (Dec 05 2023 at 16:11):

Oh. It is that simple. Thanks!

view this post on Zulip Elias Mulhall (Dec 05 2023 at 16:18):

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

view this post on Zulip Elias Mulhall (Dec 05 2023 at 16:21):

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.

view this post on Zulip Eelco Hoekema (Dec 09 2023 at 23:32):

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

view this post on Zulip Brendan Hansknecht (Dec 10 2023 at 00:39):

Definitely a bug on the roc side somehow

view this post on Zulip Brendan Hansknecht (Dec 10 2023 at 00:41):

we must have a codegen issue leading to mutation or a refcounting issue of some sort.

view this post on Zulip Eelco Hoekema (Dec 10 2023 at 09:54):

Ok. Filed a bug at https://github.com/roc-lang/roc/issues/6239

view this post on Zulip Jonas Schell (Jan 21 2024 at 19:28):

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.

view this post on Zulip Jonas Schell (Jan 21 2024 at 19:36):

Some problems I encountered:

view this post on Zulip Brendan Hansknecht (Jan 21 2024 at 20:42):

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.

view this post on Zulip Richard Feldman (Jan 21 2024 at 20:54):

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:

view this post on Zulip Brendan Hansknecht (Jan 21 2024 at 23:26):

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

view this post on Zulip Brendan Hansknecht (Jan 21 2024 at 23:26):

Happens with frac as well

view this post on Zulip Richard Feldman (Jan 21 2024 at 23:56):

interesting, we should try to find a minimal repro of that

view this post on Zulip Brendan Hansknecht (Jan 22 2024 at 00:16):

I filed one last week #6387


Last updated: Jul 06 2025 at 12:14 UTC