Unnecessary

Projects

About

Advent of Code 2024 - Day 4

Dec 4, 2024

The last few days of Advent of Code have been fairly straightforward and I have not really used any new language features of Gleam. I do feel like I am better at spotting where the list.fold function is useful, but on day 4 there was a great opportunity to make use of the use funcitonality. Before jumping in with my explanation, Isaac Harris-Holt made a video about use in Gleam which is very good, so I encourage you to watch that first before I describe how I used in on day 4.

So the use essentially takes a function, whose final argument is a callback function, and allows you to define the callback function immediately after the use line. For example the following code which doubles each number in a list using a callback function

fn example_callback() {
  list.map([1, 2, 3], fn(x) {
    2*x
  })
}

can be rewritten without the additional indentation as below.

fn example_use() {
  use x <- list.map([1, 2, 3])
  2*x
}

These are functionally equivalent returning [2, 4, 6].

In today’s problem we had to parse a 140 by 140 grid of letters into a structure we could arbitrarily search to find a combination of letters in horizontal, vertical, and diagonal directions. I decided to use a dict.Dict with the position of the character in X-Y coordinates as the key, and the letter as the value. This ended up being very easy to do with use and list.index_fold. The parsing function I wrote takes a list of lines and is shown below. I called the dictionary “map” in this example, which is probably not the best name.

fn parse(lines: List(String)) -> Dict(Point, String) {
  use map, line, y <- list.index_fold(lines, dict.new())
  let characters = string.to_graphemes(line)

  use map, c, x <- list.index_fold(characters, map)
  let p = #(x, y)
  dict.insert(map, p, c)
}

This is equivalent to writing the function below.

fn parse(lines: List(String)) -> Dict(Point, String) {
  list.index_fold(lines, dict.new(), fn(map, line, y) {
    let characters = string.to_graphemes(line)

    list.index_fold(characters, map, fn(map, c, x) {
      let p = #(x, y)
      dict.insert(map, p, c)
    })
  })
}

I like the way use simplifies the look of the function once you get used to it, however it is important to note that for each use you end up going one nested callback deeper. It can take a little time to get used to this syntax. My full solutions for all the problems are available on github.

Tags: #gleam #programming