Fork me on GitHub
#clojure-spec
<
2020-09-07
>
Ho0man18:09:05

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 lot

Ho0man18:09:06

the component/config part has to be extensible ... so different components of the system across different namespaces define their own internal config map

borkdude18:09:57

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.

borkdude18:09:04

I hoped I could get more into the structure of the regex, but it seems to be an opaque thing

borkdude18:09:10

Otherwise formulated: it is possible to get back to the int? function once it's wrapped inside this reified spec object?

(s/spec int?)

borkdude20:09:34

Maybe I'm looking for datafy on specs

tekacs20:09:40

@borkdude (s/form (s/spec int?)) => 'clojure.core/int?

tekacs20:09:02

is I think what you’re looking for

borkdude20:09:57

@tekacs That's already better!

(clojure.spec.alpha/cat :x clojure.core/int? :y user/x)

tekacs20:09:39

@borkdude

(let [{:keys [x]} (rest (s/form (:args (s/get-spec 'user/foo))))] x)
=> clojure.core/int?

tekacs20:09:01

definitely not ideal, but that works?

borkdude20:09:03

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

tekacs20:09:25

right — though you wouldn’t usually use def to define a sub-spec

tekacs20:09:57

you’d usually have done:

(s/def ::x int?)
(s/fdef foo :args (s/cat :x int? :y ::x))

borkdude20:09:59

yeah, I guess using this for linter stuff is alright, but then I might as well just inspect the raw sexpr directly

borkdude20:09:15

yes, but in that case you still get :user/x

tekacs20:09:46

right, but

(s/def ::x int?)
=> :user/x
(s/form (s/spec ::x))
=> clojure.core/int?

tekacs20:09:58

if you pass those through s/spec it resolves them

borkdude20:09:01

right, you can recursively call that on qualified keywords

tekacs20:09:10

right, precisely

borkdude20:09:15

cool, thanks!

👍 3
tekacs20:09:20

indeed you can safely pass most things through (s/form (s/spec …)) I believe

tekacs20:09:13

I’ve since moved to malli but I’m excited for this! 🙂

tekacs20:09:33

I saw that you posted in #malli too 🙂

tekacs20:09:17

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 🙂

borkdude20:09:19

@tekacs That's cool, you can use the same kind of trick indeed

👍 3
kenny20:09:05

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.

kenny20:09:19

Thank you for working on this @borkdude 😀

kenny21:09:54

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).

kenny21:09:52

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 😅

borkdude21:09:19

Note that clj-kondo can only reliably detect this right now in places where literal maps are passed

borkdude21:09:36

there are some attempts around assoc et al, but not fully worked out

borkdude21:09:29

but I guess something is better than nothing :)

kenny21:09:43

Ah, right - makes sense. And totally!

kenny21:09:38

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.

borkdude21:09:37

do-stuff can still required a map which key ::b, although it's not required, so I don't see anything wrong here

borkdude21:09:54

what we could however do is, when do-stuff has ret int? and do-moar-stuff expects a map, give an error

borkdude21:09:34

clj-kondo already has support for this, it just needs to be mapped from spec to the right format.

kenny21:09:49

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.

kenny21:09:34

A future refactoring to do-stuff could result in ::b getting removed which will cause downstream do-moar-stuff to break.

borkdude21:09:55

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

kenny21:09:17

I think that particular scenario would fall under the warning category.

borkdude21:09:28

type-wise, but maybe it could have a spec linter that gave a warning about this

borkdude21:09:06

not sure if I want to go very deep into s/keys, since spec2 will have different ways of doing

borkdude21:09:28

but basic type warnings as you type are already a free win

✔️ 3