Fork me on GitHub
#clojure-spec
<
2020-02-26
>
yuhan04:02:03

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

yuhan04:02:21

ie. this worked in spec.alpha:

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

yuhan04:02:56

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

yuhan04:02:08

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

seancorfield05:02:53

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

yuhan05:02:34

hmm.. that's slowly starting to make sense

yuhan05:02:58

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

seancorfield06:02:09

Yup, that should work.

yuhan06:02:51

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

seancorfield06:02:24

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.

seancorfield06:02:27

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

yuhan06:02:23

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

âž• 4
yuhan06:02:13

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

ben18:02:57

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?

ben18:02:20

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?

kenny20:02:45

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.

ben09:02:18

Thanks @U6GFE9HS7 - 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

ben10:02:03

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

kenny16:02:28

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.

robertfw18:02:15

@UFUDSN6BE 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))

ben18:02:33

Like stest/check but as part of my tests