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:
- Turning on and off multiline mode (making ‘.’ match ‘\n’ or not) requires weird incantations
- 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.