Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by
edited by

I am able to register a spec with an unresolvable symbol even though the docs seem to indicate this should not be an acceptable value for the registry. Is this safe? Will it keep working?

The documentation for s/def says it takes as its first argument "a namespace-qualified keyword or resolvable symbol." This is more expansive than the spec Guide, which says, "Spec names are always fully-qualified keywords."

But I recently switched a bunch of specs from keywords to symbols (fully qualified but unbound) and everything seems to work fine, from s/valid? to s/conform to s/assert, referencing the definitions in s/keys, etc.

Here is a reduced example:

user> (require '[clojure.spec.alpha :as s])
user> (ns-resolve 'com.example 'foo)
Execution error at user/eval30383 (form-init8866507034262802929.clj:53).
No namespace: com.example found
user> (s/def com.example/foo string?)
user> (s/valid? 'com.example/foo "x")
user> (s/valid? 'com.example/foo 42)
user> (s/valid? (s/keys :req ['com.example/foo]) {'com.example/foo "x"})
user> (ns-resolve 'com.example 'foo)
Execution error at user/eval30389 (form-init8866507034262802929.clj:62).
No namespace: com.example found

I am guessing this is an instance where the implementation has raced a bit ahead of the docs, and even further ahead of the guide. Either or that or maybe I'm misunderstanding what "resolvable symbol" means. Thanks for any guidance or tips!

1 Answer

+2 votes
selected by
Best answer

The registry is intended to hold two types of keys: keywords (named specs) and symbols that resolve to functions for function specs. Because the use for the latter is function specs, they can't really be effectively resolved unless the symbol resolves (hence the docs).

However, we do not validate that a symbol is resolvable at the time of s/def, and in fact we don't want to because that allows specs to be defined either before or after the function it refers to.

You should never be calling s/def with a symbol directly as in this example (it is allowed because that's how s/fdef does things under the hood).