Taming clojure.spec with expound

From Clojure 1.9 and up, the core language is guarded by various clojure.spec definitions. This means, even if you’re not using spec directly yourself, you will potentially encounter spec derived errors.

These errors, much like all errors in Clojure, are extremely verbose and make it hard to discern what the actual problem is at a glance. These errors are great for machines but not so great for humans, who happen to be the primary consumer of these errors. I consider this to be a bit of a design flaw within spec, maybe this will improve over time.

Until such potential improvements are implemented, thanks to the power and wonders of Clojure, we can use a library called expound to make our lives easier. This tool is inspired by Elm and it shows. Without expound and this incorrect syntax:

We get this beauty of an error from Clojure itself:

Can you tell what’s going on? Probably after a little bit of time if you know what you’re looking for. Imagine this in a much more complex case though, let’s say within a few layers of macros. Now here’s the same response through expound:

That’s much better! Suddenly you don’t have to run a REPL in your brain to understand what went wrong, the machine is telling you exactly what is wrong, where and what you can do instead. It may not be as succinct as Elm, but the information at the start is just as useful.

Integration

Luckily, expound happens to be extremely easy to use. Hopefully we can make that easier by including it by default in a lot of beginner friendly code too. The README does a great job of explaining how to use it.

Replace calls to clojure.spec.alpha/explain with expound.alpha/expound and to clojure.spec.alpha/explain-str with expound.alpha/expound-str.

If you don’t use explain directly and you’d like all spec errors to be run through expound (including those from Clojure the language), then you can hook it in globally like the following snippet. I’d recommend running this within your (ns user) before your REPL loads or in the main ns of your application before it starts up.

Now any spec error generated from here on out will be formatted for human consumption by expound, excellent!

I doubt I’m alone in thinking that I’d love this to be the default within Clojure or at least extremely widespread in it’s usage. Much like figwheel for ClojureScript projects, we would always use expound alongside our specs. Maybe CIDER could be a good entry point for this addition.