Hi everyone,
Can I write a multi-spec for the component/config
segment of this data based on the component/type
which is not part of component/config
's corresponding value? :
{:component/type [:instance-manager :v-2.0.0]
;;-----------------------------------------;;
:component/config {,,,
}
;;-----------------------------------------;;
:component/deps {}}
(I'm using spec1)
Thanks a lotthe component/config
part has to be extensible ... so different components of the system across different namespaces define their own internal config map
Is it possible to inspect an evaluated regex spec?
(def x int?)
(s/fdef foo :args (s/cat :x int? :y x))
(prn (::s/op (s/cat :x int?))) ;; :clojure.spec.alpa/pcat
(prn (::s/op (:args (s/get-spec 'user/foo)))) ;; nil
Use case: I'd like to know what the x
resolved to. Just inspecting the form won't tell me that.I hoped I could get more into the structure of the regex, but it seems to be an opaque thing
Otherwise formulated: it is possible to get back to the int?
function once it's wrapped inside this reified spec object?
(s/spec int?)
Maybe I'm looking for datafy on specs
@borkdude (s/form (s/spec int?)) => 'clojure.core/int?
is I think what you’re looking for
@tekacs That's already better!
(clojure.spec.alpha/cat :x clojure.core/int? :y user/x)
(let [{:keys [x]} (rest (s/form (:args (s/get-spec 'user/foo))))] x)
=> clojure.core/int?
definitely not ideal, but that works?
I kinda wish that I could have user/x
resolved to clojure.core/int?
as well, since this information is available in the spec itself
right — though you wouldn’t usually use def to define a sub-spec
you’d usually have done:
(s/def ::x int?)
(s/fdef foo :args (s/cat :x int? :y ::x))
yeah, I guess using this for linter stuff is alright, but then I might as well just inspect the raw sexpr directly
see also https://github.com/wilkerlucio/spec-inspec/blob/master/test/com/wsscode/spec_inspec_test.clj#L10
Thanks. Added link to https://github.com/clj-kondo/inspector/issues/4
yes, but in that case you still get :user/x
right, but
(s/def ::x int?)
=> :user/x
(s/form (s/spec ::x))
=> clojure.core/int?
if you pass those through s/spec it resolves them
right, you can recursively call that on qualified keywords
right, precisely
cool, thanks!
indeed you can safely pass most things through (s/form (s/spec …)) I believe
@tekacs purpose of this exercise: https://gist.github.com/borkdude/c0987707ed0a1de0ab3a4c27c3affb03
I’ve since moved to malli but I’m excited for this! 🙂
I saw that you posted in #malli too 🙂
I also forked aave for malli to add CLJS support and some additional features I haven’t seen elsewhere (https://github.com/tekacs/aave), so I should probably pay attention to the clj-kondo type spec 🙂
@tekacs That's cool, you can use the same kind of trick indeed
(code updated here: https://gist.github.com/borkdude/c0987707ed0a1de0ab3a4c27c3affb03)
This is awesome. Support for s/keys would be a huge productivity win. We have many functions that take large maps as params and it's so easy to forget or misspell one.
Thank you for working on this @borkdude 😀
:)
OMG
So freaking cool. An optional toggle for something like https://github.com/bhauman/spell-spec would epic (I could see some people not liking it).
I wonder how many places in our code base we have a :req key and aren't actually passing it but it happens to magically work in prod 😅
Note that clj-kondo can only reliably detect this right now in places where literal maps are passed
there are some attempts around assoc et al, but not fully worked out
but I guess something is better than nothing :)
Ah, right - makes sense. And totally!
Wonder how far you could go with it... e.g.,
(defn do-stuff
[x])
(s/fdef do-stuff
:args (s/cat :x int?)
:ret (s/keys :req [::a]))
(defn do-moar-stuff
[m]
)
(s/fdef do-moar-stuff
:args (s/cat :m (s/keys :req [::a ::b]))
:ret int?)
(-> 1
(do-stuff)
(do-moar-stuff))
Technically seems like you could infer that do-moar-stuff will (probably) not get passed the right stuff, assuming the specs are correct.do-stuff can still required a map which key ::b
, although it's not required, so I don't see anything wrong here
what we could however do is, when do-stuff has ret int? and do-moar-stuff expects a map, give an error
clj-kondo already has support for this, it just needs to be mapped from spec to the right format.
The specs for do-stuff and do-moar stuff don't align and they should. By happenstance, do-stuff could always return ::b but that's just luck.
A future refactoring to do-stuff could result in ::b getting removed which will cause downstream do-moar-stuff to break.
the clj-kondo "type system" will only complain if it's sure something is wrong, so in this case it would not complain I think
I think that particular scenario would fall under the warning category.
type-wise, but maybe it could have a spec linter that gave a warning about this
not sure if I want to go very deep into s/keys, since spec2 will have different ways of doing
but basic type warnings as you type are already a free win