Advent of Clojure — Preamble

Paula Gearon
5 min readJan 20, 2018

--

Welcome! I’m really writing this for me, but I’m happy to have you along.

This is where I start setting up how I was to approach the Advent of Code, written in Clojure, as described in the last post.

My intended audience here is one of two groups. The first, are people who have recently learnt Clojure, and are trying to figure out how to encode their ideas in this new language. I will not be using any more involved features, like protocol dispatch, macros, transducers, software transactional memory, agents, or any number of the more advanced features found in Clojure. The nature of the Advent of Code puzzles is often simple data manipulation, and each solution fits naturally into a single file. Real world projects will lend themselves to many of the more advanced features in Clojure, but that isn’t needed here.

The second group is comprised of more experienced Clojure programers who are simply curious about how someone else approached these problems. I’m sure I will cause several of you to laugh at my naïveté.

Setup

The questions in Advent of Code are stories, which are provided along with the user’s input. The website only needs to see the answer, and does not need to see any code. For the sake of speed, a solution can often be created at the REPL prompt. I made heavy use of the REPL to create my own solutions, but once a question became more complex, it really does demand a source file.

I often recommend that people new to Clojure use Leiningen to manage their project. When Clojure runs on the JVM it requires a classpath to be set up, even when nothing more than the basic Clojure runtime is needed. Leiningen can do this. When building a project, a directory framework should be set up, external libraries should be downloaded and added to the classpath when testing and running, and all of this should be packaged up into a deployable file when the the project is released. Leiningen can do all of these tasks, and more. Other systems (like Boot) are also available, but I will leave that up to you to decide on.

When using Leiningen, a new project called “aoc” can be set up with a command like:

lein new app aoc

This creates a new application directory framework in a directory called “aoc”.

Another, more recent option is to use the new Clojure installations available for Mac and someLinux systems (on a Mac, use the Homebrew system to install “clojure”. On Linux, use whichever package manager is appropriate for that system). This system will run programs easily with just the command:

clojure package.name

A configuration file (deps.edn) can be used to set up external libraries, but that is more that is required for the Advent of Code problems. This approach is good for running simple programs, but does not provide the capabilities of flexibility needed for production code. I won’t be using this approach.

Project

Once the aoc directory was created, I wanted it to be able to run each of my solutions easily. Each solution was to be in its own file, including a -main function so that it could be run as an independent program. But I also wanted to make it easy to run them all, as a suite of programs. To this end, I created a central program based on the default “core” namespace. To set this up in the project, I edited the project.clj file to look like this:

The default file comes with some other lines, but they really aren’t needed for this project, and I removed them. What’s left is some boilerplate, and just a couple of project specific lines. The name and version number for the project is on line 1. Line 2 is the description, and lines 3–4 provide the license (a friendly EPL). Line 5 says that I want to use the latest version of Clojure (as of this writing, this is 1.9.0).

It’s on lines 6 and 7 that we see something that we need to set up for this project. Line 6 says that the main program is in the aoc.core namespace. Having this line means that you can the program with the simple command:

lein run

Line 7 makes all files in the “resources” directory available for programs to easily load. This is where the data files will be kept.

Now that the project is set up, the main program can be written.

Core

All source files are found under the “src” directory. Clojure namespaces are “dot separated” similarly to Java classes in packages, where each element of the package name indicates a directory, and the final part is the filename. So the aoc.core namespace will be found in a file called: src/aoc/core.clj

What this program needs to do is to find programs that provide solutions for each day, and to run them in turn. There are lots of ways to do that, but I intend to write each program in namespaces indicating the day:

aoc.day1, aoc.day2, aoc.day3, …

Sticking to this pattern makes it easy to find the days, and also allows me to include other files in that aoc directory without having to be concerned about it being run as an independent program.

To do this, the code starts with n at 1 and goes up from there, trying to load a namespace of the name aoc.dayn until this fails. For each namespace that can be loaded, it then runs the -main function in that namespace.

The namespace-load function on lines 3–9 takes a namespace symbol, and tries to load it (line 8). If this works, then the require will return nil. On failure, an exception is thrown, so this is caught, and a value of :error is given. This is then wrapped in a call to not, which means that a successfully loaded namespace returns true, while a missing namespace will return false.

The -main function (lines 11–24) is the entry point for the program. Line 16 indicates that the code will be run repetitively with n set to each value in an infinite sequence of numbers, returned by (range), dropping the 0 from the start of the sequence with rest. Line 17 creates a symbol from the number n, so that it looks like aoc.day1, and line 18 says that the loop should finish when attempting to load this namespace fails. Note that loading the namespace is a stateful operation, though it doesn’t update any known values (or “vars” in Clojure). It does, however, create the given namespace and populates it with whatever is found in that file.

Lines 19 and 20 just print what the program is trying to run now (the flush means that this is printed despite not having a newline character). Line 21 access the namespace, and line 22 looks for the -main function in it. If it’s found, then it gets called, passing along any arguments given to this program. If no -main function is found, then it prints an error, but that’s not supposed to happen. 😄

Wrap Up

With all of this in place, I was ready to start in on the Advent of Code puzzles. The next post is Day 1.

--

--