I'm trying out spec2. I'm noticing that schema doesn't seem to work if one of the keys was defined as another "qualified keyword symbolic spec" with s/def
(s/def :x/a int?)
(s/valid? (s/schema [:x/a]) {:x/a 34}) ; works
(s/def :x/b :x/a)
(s/valid? (s/schema [:x/b]) {:x/b 34})
"IllegalArgumentException No implementation of method: :conform* of protocol: #'clojure.alpha.spec.protocols/Spec found for class: clojure.lang.Keyword
clojure.core/-cache-protocol-fn (core_deftype.clj:585)
clojure.core/-cache-protocol-fn (core_deftype.clj:577)
clojure.alpha.spec.protocols/eval21772/fn--21773/G--21753--21784 (protocols.clj:11)
clojure.alpha.spec.impl/schema-impl/reify--23068 (impl.clj:435)
clojure.alpha.spec/valid? (spec.clj:358)
clojure.alpha.spec/valid? (spec.clj:350)
clojure.alpha.spec/valid? (spec.clj:353)
clojure.alpha.spec/valid? (spec.clj:350)
clojure.lang.Compiler.eval (Compiler.java:7700)
clojure.lang.Compiler.eval (Compiler.java:7655)"
(s/register :x/c (s/spec :x/a))
(s/valid? (s/schema [:x/c]) {:x/c 34}) ;;works
is this a bug?I dug into this a little more, I made an attempt at a fix https://github.com/jjttjj/spec-alpha2/tree/indirect-schema-select.
I totally understand the state of spec2 is "extremely alpha" and that there likely just are no actual decisions made on this stuff. But I wanted to do a mind dump about it while it's fresh on my mind.
To reiterate what I'm trying to do, I want to use spec2 to spec an entity that "references"/"contains" another entity. e.g. a :order/user "is a" ::user to some extent. In spec1 you essentially do this with (s/def :order/user ::user). [I've been referring to these specs as "indirect" specs based on Rafael's answer in the ask.clojure thread linked below, but would be happy to be told a better name.]
In spec2 I believe you want ::user here to be a schema (not a select). And, then at a a higher level you would make the select to describe the nested stuff you need. But both the schema and select implementation weren't properly resolving specs causing none of this to work.
But I'm pretty curious if this was intentionally left broken to eventually support an alternative to (s/def :order/user ::user) to support the same thing. In https://ask.clojure.org/index.php/12228/spec2-bug-on-nested-schema-specs?show=12228#q12228, Alex says:
> it's very much undecided whether nested schema specs will work like this
Because not having a way to do:
(s/select ::order
[:order/id
:order/user
{:order/user [:user/id]}])
without copy/pasting the ::user spec to :user/order seems like a glaring omission. Was there meant to eventually be some other spec op to handle these "indirect reference" specs? Or maybe this is just an actual bug an I'm overthinking it.Spec 2 is nowhere near complete and has a number of bugs. It is not ready for production usage.
Yeah I figured that was likely, just doing a sanity check
For a while, long ago, we were tracking Spec 2 on a branch to see how much our code would have to change (from Spec 1), because we wanted to leverage some of the new features. Eventually, we gave up because it wasn't going to get solid enough to merge in any time soon.
In your code, if you (s/def :x/b (s/spec :x/a)) I assume it works?
good to know thanks. I just happened to hit something where the schema/select stuff solves it elegantly so I figured I would give it a spin. > In your code, if you (s/def :x/b (s/spec :x/a)) I assume it works? That gives:
ExceptionInfo Unable to def :x/b, unknown spec op: clojure.alpha.spec/resolve-spec {:k :x/b, :form (clojure.alpha.spec/resolve-spec :x/a)}
The register form is the one from my first example is the only one I can get working
(s/register :x/c (s/spec :x/a))
I'm trying to use the https://github.com/arachne-framework/valuehash library for hashing Clojure data -and it seems like a good fit for me. But when I run my test suite, I get an error that seems to be tied to the (broken? incomplete?) https://github.com/arachne-framework/valuehash/blob/master/src/valuehash/specs.clj. The (partially elided) error is:
ERROR in (generate-deterministic!) (alpha.clj:289)
Uncaught exception, not in assertion.
expected: nil
actual: clojure.lang.ExceptionInfo: Unable to construct gen at: [:input-stream] for: input-stream?
#:clojure.spec.alpha{:path [:input-stream], :form valuehash.specs/input-stream?, :failure :no-gen}
at clojure.spec.alpha$gensub.invokeStatic (alpha.clj:289)
clojure.spec.alpha$gensub.invoke (alpha.clj:279)
...
clojure.spec.alpha$regex_spec_impl$reify__2503.conform_STAR_ (alpha.clj:1710)
clojure.spec.alpha$conform.invokeStatic (alpha.clj:171)
clojure.spec.alpha$conform.invoke (alpha.clj:167)
clojure.spec.test.alpha$spec_checking_fn$conform_BANG___3021.invoke (alpha.clj:132)
clojure.spec.test.alpha$spec_checking_fn$fn__3023.doInvoke (alpha.clj:151)
clojure.lang.RestFn.invoke (RestFn.java:421)
valuehash.api$sha_1.invokeStatic (api.clj:52)
valuehash.api$sha_1.invoke (api.clj:49)
It seems the author omitted the generator for some specs and somehow the https://github.com/cognitect-labs/test-runner is requiring them. Does anybody know how to either suppress this kind of testing or (ugh) easily provide the generator in my code? My understanding based on the https://clojure.org/guides/spec#_testing was that these kinds of fspec tests were only run when explicitly requested with https://clojure.github.io/spec.alpha/clojure.spec.test.alpha-api.html#clojure.spec.test.alpha/check. Nowhere in my codebase am I running that command; I don't even require the test.check namespace.Function specs have their arguments checked when you instrument them.
They have their results checked when you stest/check them.
If the arguments have fspecs themselves, then they are generatively tested as part of instrumented argument checking, if I recall correctly.
That's why it's generally safer to avoid overly specifying higher-order function specs.
As an aside, I thought the whole Arachne project was abandoned (and incomplete)... and I see this spec has a misspelled symbol so it is ignored anyway: https://github.com/arachne-framework/valuehash/blob/master/src/valuehash/specs.clj#L18
Note that the valuehash.api itself is requiring the specs ns: https://github.com/arachne-framework/valuehash/blob/master/src/valuehash/api.clj#L3 and sha-1 invokes digest and that has a function argument that has an fspec...
Cognitect's test-runner does nothing special with specs.
However, if your test suite contains (s/instrument) then you are instrumenting everything that has a spec, including code in libraries, if their specs have been loaded (and valuehash.api loads its incomplete specs which means they are not opt-in... which is bad library practice, IMO).
At this point @cch1 your best bet might be to fork valuehash and remove valuehash.specs from the :require in valuehash.api, and work with that fork...
Thanks for that solid advice. Seems easy enough to fork.
...and while the lib might have been abandoned, it just got a good recommendation https://clojurians.slack.com/archives/C03S1KBA2/p1734446525391189.
(I'm still in the exploring stage and the bake-off with hasch is just starting. I like that valuehash has a small dependency footprint compared to hasch, so I'm starting with it. I've already ruled out clj-uuid/v5 as it's not =-compliant)
I wonder how many of those folks recommending it have actually used it?
And, just to check, do you have s/instrument in your test suite?
I might have s/instrument somewhere -and if I do I'll be disappointed to turn it off.
You can always instrument just your code rather than "the entire world" 🙂
I have always struggled to selectively unstrument stuff. The timing of when instrument and unstrument are run and when the subject code is loaded is just too brittle and opaque. I dread tracking down the right place and timing to unstrument the lib but leave my stuff alone.
In next.jdbc, I only instrument specific functions: https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/specs.clj#L270-L297
That allows users (or tests) to selectively turn instrumentation on and off for just the next.jdbc library without affecting anything else.