Clojure, Prismatic Schema, Components, and ClassNotFoundException

I’ve been using clojure¬†to some extent for about 3 years now. Up until very recently, I haven’t had one single reason to even look at the whole defrecord thing.

Recently, I came to the reluctant conclusion that sometimes things like type hints are good things. Usually when the functions that can benefit from them involve parameters that are just too complicated to work well with language standard type hints. So I finally broke down and learned me some Prismatic Schema.

Which, for the record, is wonderful stuff. Even though I haven’t used it for anything but code documentation . Yet.

One of my favorite parts is that it seems to work best with plain ol’ data structures like maps and vectors.

Somewhere in the same time frame, I started switching from Stuart Sierra’s Workflow Revisited pattern to his shinier Components library.

Which, for the record, is also great. It has drastically simplified a lot of my code. And made it glaringly obvious which parts of my start sequence are truly awful.

These two great tastes taste great together.

Until you need to refer to a record in a namespace that’s different from the one where it was defined. Such as specifying that your Session record in foo.session.core has a member that’s an instance of Database, defined in foo.persist.core.

That’s when the compiler starts throwing ClassNotFoundException. And, if you’re like me, life is miserable, because you just changed a whole slew of¬†namespaces without carefully eval’ing each change because, hey, this is just grunt monkey coding. So you can’t even get to a REPL to try to figure out what’s going on.

This is a problem that’s plagued clojure since the day defrecord was introduced. The main result that google kept pointing me at is a stackoverflow question that boils down to “You have to import it.” Along with assurances that this had been in 1.4.

The import line was where the error was coming from.

The second major hint was to be sure to replace any – in the names with _, because this is java land. I was definitely doing that wrong (and this part is absolutely vital), but it didn’t help.

The magical equation (at least on 1.6.0) seems to be:

  1. Add a (:gen-class) to the ns declaration at the top of the namespace with the record that you want to use elsewhere
  2. In the ns directive for the namespace that’s going to actually use that record, add a reference to its namespace in the (:require) section
  3. Put the (:import) section below (:require)
  4. Double-check that you remembered to replace dashes with underscores.

There may be a slightly less magical incantation that I’m missing, but I haven’t been able to make it work any other way.

TODO: get on IRC and verify this.