I’ve been experimenting with clojure.spec on a new web project, as a way of validating data coming in from the client. We’re using JSON API as the lingua franca between the server and client, which adds a not-insignificant amount of ceremony and overhead to the JSON payload. So once I got one namespace’s endpoints spec’ed correctly, the obvious next step was to generalize it with a function that looked like this (edited for brevity):

What we’re accomplishing here with (keyword api-ns "attributes") is building a keyword that looks like :my-project.api.users/attributes. Or at least, that’s what I thought we were doing; here’s the error I ran into:

CompilerException java.lang.AssertionError: Assert failed: k must be namespaced keyword or resolvable symbol

That’s weird — (keyword api-ns "attributes") on the REPL is building a clojure.lang.Keyword like I expected it to, so why is an assert that it’s a keyword failing?

The answer lies in how the macro behind clojure.spec/def is implemented. Macroexpanding (s/def (keyword "spec-test-ns" "name") string?) gives you:

So since it’s quoteing our input, it’s not resolving to the keyword before the assert. The solution I came to is to build a macro that unquotes my keyword building before s/def is called:

I generally prefer non-macro solutions when available, but as of now, this seems like the only way to DRY up this particular pattern.