Fork me on GitHub

Is this supposed to work in spec 2?

(s/def ::some-keyword ::some-other-keyword)
When I try to validate against ::some-keyword I get this exception:
java.lang.IllegalArgumentException: No implementation of method: :conform* of protocol: #'clojure.spec-alpha2.protocols/Spec found for class: clojure.lang.Keyword
I can fix the symptoms by doing this instead of s/def, but the fact that I have to do a special dance makes me suspect that I might be working against the tool's grain
(s/register ::some-keyword (s/resolve-spec ::some-other-keyword))


I get that spec2 is stricter than spec1 in terms of separating symbolic specs from spec objects. From the documentation ( (emphasis mine) > s/def is a helpful wrapper macro for s/register that understands how to interpret all kinds of symbolic specs (not just spec forms), including symbols and sets, which will be wrapped in the helper s/spec spec op. I was hoping that ::some-other-keyword would qualify as a symbolic spec, but maybe not.


I believe aliased Specs are supposed to work but there are situations where you can forward reference in Spec 1 but you cannot yet forward reference in Spec 2.


I assume ::some-other-keyword is defined after ::some-keyword here?


Actually, it's imported from another ns in my real code, shouldn't have tried to simplify that away


user=> (require '[clojure.spec-alpha2 :as s])
user=> (s/def ::one pos-int?)
user=> (s/def ::two ::one)
user=> (s/valid? ::two -1)
user=> (s/valid? ::two 1)




There must be something else going on then


Let's see if I can build a minimal example that reproduces the behaviour I observe


In that simple case, it does let you forward reference FYI -- I tried it in a fresh REPL session with the defs of ::one and ::two swapped and it still worked.


Interesting. Thank you for the handholding 😊, will try to figure out what I'm doing differently and report back


With the caveat, again, that there are numerous bugs in Spec2 still at this point. And it sounds like function specs will change dramatically before they're done, according to Alex's latest blog point (and s/and may become non-flowing -- which would be another departure from Spec1). While I was working against the Spec2 branch for a while, things would break between various updates on master, sometimes intentionally but often unintentionally. It's very much a moving target right now.


I had hoped to migrate our (large) codebase from Spec1 to Spec2 but, in reality, I think we're "stuck" with Spec1 in our existing code at this point and we'll just adopt Spec2 for new code (depending on how Spec1 and Spec2 interop -- because they don't, right now).


Eventually, we'll convert everything over but I suspect we'll do it piecemeal over time.


Mine is just a hobby project, but yes, I might have been over eager there 🙂


Maybe I should just switch back to spec 1, and follow the advice above (<>) to make all my specs :opt rather than :req, merging in :req at the last minute to emulate schema and select.


That way I'd also get orchestra and expound, which would be nice


And probably other libraries and tooling integration that I'm not aware of yet.


> make all my specs :opt rather than :req, merging in :req at the last minute to emulate schema and select. I hadn’t seen the discussion of that above, but that’s pretty much what I’ve come to independently for a large-ish set of domain specs I’ve been working on. Seems to be a decent approach.


Along with a couple of helper functions to eg build a selection from a schema less verbosely by passing in the base schema and the list of attributes that should be required (in the simple cases), and by passing in the base schema and a full tree of requiredness (for complex cases).