clojure-spec 2022-06-24

Hope I’m not spamming people, but I would love to get some feedback on a tool we made to help with creating spec generators: https://git.sr.ht/~jomco/proof-specs proof-specs can be run as a leiningen alias or from the repl and will exercise all data specs (keyword specs) in a set of namespaces and report on any errors. We’re using it ourselves as part of our test suite to ensure we always write generating specs and to make it easier to find out which part of complex specs are broken. Thanks for any feedback :-)

Main reason we wrote the thing is that sometimes spec generators just error out with very little context making it hard to figure out which specs even work. proof-specs will at very least give you a list of every spec that won’t generate which helps a lot in figuring out what’s going on.

This might be a silly question but is there a s-exp spec that can be applied recursively?

But I guess you’re free to make a function that does recursive stuff and use it in a spec? 🙂

yes, also specs can be applied recursively even if it's silly 👺

@raymcdermott What are you looking for? Something like tree-seq + spec? This is what grasp basically does: https://github.com/borkdude/grasp

ok - thanks, I remember talking about it with you but I've never looked at the code 👀

to be more straightforward: I'm looking to conform all of Clojure. That's what I'm really looking to do.

@borkdude one small thing I noticed in your reify spec is that you use (s/cat :reify #{'reify} ... why do you use a set rather than just the symbol 'reify? Cos that's what I do and it works but maybe set gets you something else that I'm missing?

Because of how spec works?

user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/valid? 'foo 'foo)
Execution error at user/eval154 (REPL:1).
Unable to resolve spec: foo
user=> (s/valid? (s/cat :foo 'foo) ['foo])
Execution error at user/eval156 (REPL:1).
Unable to resolve spec: foo
user=> (s/valid? #{'foo} 'foo)
true
user=> (s/valid? (s/cat :foo #{'foo}) ['foo])
true

I.e. I want to match the literal symbol 'reify

yes ... actually it's neater than #(= 'reify %)which is the ugly thing I have 🙇🏼

I use sets for comparison all the time. it's great!

yeah, seems a ton more elegant and more transparent than a function

👌 1
Alex Miller (Clojure team) 2022-06-27T20:14:51.089499Z

oh but it is a function ;)

👏🏻 1

Sometimes it can make sense to make it a var as well. Like

(def valid-foo? #{:x :y :z})
(valid-foo? :x) ;=> :x

😍 2
Alex Miller (Clojure team) 2022-06-24T22:20:08.027779Z

No, and I think you can quickly come up with many reasons why

Sure. Like I said, was a silly question. I just wanted to make sure I hadn't missed anything before moving on other options.

Eg (s/cat symbol? s/+ any?) but recurring

You can give it a name, then recur to this name:

(s/def ::foo
  (s/cat :k keyword?
    :v map?
    :others (s/? ::foo)))
=> :user/foo
(s/valid? ::foo
  [:a {} :b {} :c {}])
=> true
(s/valid? ::foo
  [:a {} :b {} :c])
=> false
(s/valid? ::foo
  [:a {} :b {} :c []])
=> false

ha, yes - actually that's what we ended up doing! 🙂

[on the phone so syntax ain't great]