clojure-spec

joost-diepenmaat 2022-06-24T16:18:38.179699Z

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

joost-diepenmaat 2022-06-24T16:24:27.683979Z

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.

ray 2022-06-24T22:09:39.358599Z

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

reefersleep 2022-06-27T09:36:11.446449Z

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

ray 2022-06-27T09:39:09.813489Z

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

borkdude 2022-06-27T11:38:23.415219Z

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

ray 2022-06-27T12:10:16.966039Z

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

ray 2022-06-27T12:13:13.073179Z

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

ray 2022-06-27T18:59:50.158619Z

@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?

borkdude 2022-06-27T19:01:53.929499Z

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

borkdude 2022-06-27T19:03:01.865739Z

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

ray 2022-06-27T19:20:24.694149Z

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

reefersleep 2022-06-27T19:51:27.966659Z

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

ray 2022-06-27T19:52:58.954469Z

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
ray 2022-06-27T20:25:32.930269Z

ho ho

reefersleep 2022-06-28T07:38:16.356659Z

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

ray 2022-06-24T23:18:06.952019Z

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

ray 2022-06-24T22:11:25.093709Z

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

souenzzo 2022-06-27T17:01:10.415309Z

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

ray 2022-06-27T18:55:41.569019Z

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

ray 2022-06-27T18:56:00.837159Z

thank you @souenzzo

ray 2022-06-24T22:12:05.293739Z

[on the phone so syntax ain't great]