cosarara.me

The blog

Advent of Code Haskell

It is that time of the year again, Advent of Code time. I love the puzzles by themselves, especially as they become trickier and trickier, but I also take the opportunity to explore programming languages I don’t usually use that much.

If I don’t have much time that day, or I’m feeling a bit frustrated, I might solve it in python only (the language that usually comes more naturally to me), but otherwise, I want to try other stuff.

Today I did Haskell. It’s not the first time I try it, I had used it back in 2019 for a few of the puzzles, and then in 2022 I solved day 21 with a shell script that generated haskell source code from the input, and getting the solution by running that haskell program. If I remember correctly, back in the day I learned it through the book Learn You a Haskell for Great Good!.

Damn it has been a bit frustrating. I had no haskell toolchain installed but GHCup made it easy to set up (taking a lesson from rustup?), and with a look at the First Steps and the 2019’s solutions I had a hello world that read stdin with getContents.

Immediately I realized I wanted to use regular expressions to solve the problem (of course not the only way to solve it, but I wanted to).

Google brings me to https://wiki.haskell.org/index.php?title=Regular_expressions: A thousand options, decision paralysis.

The second result is https://hackage.haskell.org/package/regex-compat-0.95.2.1/docs/Text-Regex.html:
inscrutable. Can I import Text.Regex and then use matchRegexAll? No, this is not part of base.

Searching regex on hackage: The first result is https://hackage.haskell.org/package/regex which seems promising, but then the API docs link http://hs.regex.uk/ is broken. regex.uk resolves, but the subdomain names don’t. Welp.

Another google result points to regex-tdfa, the second result from hackage. Ok, seems promising enough. The GHCup first steps show me how to cabal install --lib regex-tdfa --package-env ., and then I can import Text.Regex.TDFA in my code and use the functions listed in the readme.

I don’t particularly like the API, using =~ for everything and then having to specify the type of the result if the compiler can’t figure it out, but it gets me solving the problem. (gotcha: \d for matching digits does not work).

Then I get to part 2, and while trying to understand why my code is not working I find that:

  1. Turning on and off multiline mode (making ‘.’ match ‘\n’ or not) requires weird incantations
  2. The library does not do find/replace.

But ok, I can work around it and finally get my code working.

Also, this works:

main :: IO ()
main = do
  input <- getContents
  let muls = sum
       $ map (\(a, b) -> a * b)
       $ mapMaybe intPair
       $ getMuls
       $ removeDisabled input

  print muls

But this does not:

main :: IO ()
main = do
  input <- getContents
  let muls = sum
     $ map (\(a, b) -> a * b)
     $ mapMaybe intPair
     $ getMuls
     $ removeDisabled input

  print muls
b.hs:32:3: error:
    The last statement in a 'do' block must be an expression
      let muls = sum
   |
32 |   let muls = sum
   |   ^^^^^^^^^^^^^^

I’m a python fan, I think significant indentation is good and fine, and I understand now why one works and the other doesn’t (if it’s less indented than the start of the assignment, it means other let assignments are comming. I think). But uuuuuuuuugh it took me so long.

Tired.

—cosarara

Got any comments? Send me an email: blog@cosarara.me