clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
yuhan 2020-02-26T04:48:03.040Z

I was playing around with spec2 and noticed that it doesn't allow for using local bindings in definitions

yuhan 2020-02-26T04:48:21.040400Z

ie. this worked in spec.alpha:

(let [max-v 10]
  (s/def ::foo (s/int-in 1 (inc max-v)))
  (s/valid? ::foo 20))

yuhan 2020-02-26T04:48:56.040900Z

but not in spec2, where it would throw an error "Unable to resolve symbol max-v"

yuhan 2020-02-26T04:50:08.041600Z

Is this a deliberate design decision or was it ever officially supported in spec.alpha in the first place?

seancorfield 2020-02-26T05:41:53.043600Z

@qythium Spec 2 draws a much sharper line between symbolic specs and spec objects. Spec 2 has new facilities for creating specs programmatically that allow you to create such a spec, but in a different way.

seancorfield 2020-02-26T05:46:00.044Z

You can read about the design decisions here https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha @qythium

yuhan 2020-02-26T05:59:34.044600Z

hmm.. that's slowly starting to make sense

yuhan 2020-02-26T05:59:58.045Z

So I would do this instead?

(let [max-v 10]
  (s2/register ::foo
    (s2/resolve-spec `(s2/int-in 1 (inc ~max-v))))

  (s2/valid? ::foo 20))

seancorfield 2020-02-26T06:05:09.045300Z

Yup, that should work.

yuhan 2020-02-26T06:08:51.046600Z

aha, so it's roughly like (s2/def kwd expr) is macro sugar for (s2/register kwd (s2/resolve-spec expr))`

seancorfield 2020-02-26T06:11:24.048700Z

Yes, if you look at the source https://github.com/clojure/spec-alpha2/blob/master/src/main/clojure/clojure/alpha/spec.clj you'll see def calls register and several macros use resolve-spec to produce spec objects from symbolic forms.

seancorfield 2020-02-26T06:12:27.049500Z

I don't have any code handy but defop is also handy for building new spec predicates.

yuhan 2020-02-26T06:13:23.049800Z

That actually makes a lot more sense than the nested macros in the original Spec 🙂

âž• 1
yuhan 2020-02-26T06:14:13.050600Z

which I recall macroexpanding to trace the logic and eventually just treated as magic

yuhan 2020-02-26T06:14:34.050800Z

thanks!

ben 2020-02-26T18:13:57.052300Z

If I have written a s/fdef for a function, is there an easy way to generate property tests for that function (as part of a test suite), without having to manually (re)write all the generators?

ben 2020-02-27T09:59:18.055400Z

Thanks @cjsauer - I am aware of test.check, but as I understand it, I will have to write my own generators again, rather than using fdef’s :args to check the function

ben 2020-02-27T10:00:03.055600Z

Thanks @kenny - this looks like what I was imagining. Why do some not recommend it?

kenny 2020-02-27T16:01:28.056400Z

I think the argument is that they should run at a different time than regular unit tests because they are different. I don't have a problem with that -- run them in whatever way fits your company's workflow. These sort of tests are invaluable though. You should certainly run them before deployment. Having one test runner (https://github.com/lambdaisland/kaocha for us) is quite nice. We have a large codebase with thousands of tests. Running gen tests with our unit tests has not caused us any substantial pain. They typically take 1s or less to run. The more complex functions with custom gens can take longer. The results of a gen test do not fit well into the expected/actual framework clojure.test uses though. That is certainly another downside.

robertfw 2020-02-27T18:57:15.057100Z

@ben606 re: generating args. I use the following code to generate args from an fdef:

(defn arg-gen
  "Creates an argument generator for a given function symbol"
  [sym]
  (-> sym s/get-spec :args s/gen))

ben 2020-02-26T18:14:33.053Z

Like stest/check but as part of my tests

ben 2020-02-26T18:28:20.053900Z

I saw this, but my understanding is that is runs the tests (like test.check/quick-check) rather than creating a test (`deftest`). Have I misunderstood?

cjsauer 2020-02-26T19:33:16.054700Z

@ben606 maybe you’re looking for something like defspec https://github.com/clojure/test.check/blob/master/doc/intro.md#clojuretest-integration

kenny 2020-02-26T20:26:45.055100Z

Although some don't recommend it, we do run spec's "check" in deftests using an "internal" (but public) library: https://github.com/Provisdom/test/blob/a61266ab281580af88fd394e9896cb85332cc5d7/src/provisdom/test/core.clj#L330 This will let you write something like this

(deftest my-fn-gen-test
  (is (spec-check `user/my-fn)))
which will run & report st/check errors.