Fork me on GitHub
#clojure-spec
<
2016-05-28
>
cfleming01:05:56

@alexmiller: You mentioned you have a spec for spec. Any interest in showing that? It would help with adding support in Cursive.

Alex Miller (Clojure team)01:05:25

It's almost certainly pretty buggy so it's not something I really want to publish yet

Alex Miller (Clojure team)01:05:46

I have turned on instrumentation with it and had it actually find bugs while I was working though :)

cfleming01:05:23

Yeah, I’m planning to merge in the validation code I demoed at the conj, having real-time validation in Cursive for specs would be nice.

Alex Miller (Clojure team)01:05:31

I can send it to you privately if you want

cfleming01:05:20

And using specs to help with completion is probably nice too, although I need to work with it more to see what’s possible.

cfleming01:05:36

Ok, thanks. I’m not in a big rush, I won’t get to it for a week or two probably.

cfleming01:05:50

But I’d be interested to see it.

sekao01:05:25

anyone have an example of using fdef with variadic args?

sekao02:05:45

ahh i guess it’s something like (s/cat :args (s/* string?))

Alex Miller (Clojure team)11:05:16

@sekao: yep although you don't even need the s/cat there just '(s/* string?)' would be sufficient

francesco11:05:14

how could I write a spec for, say, clojure.core/conj that enforces that in (conj coll x) x satisfies the same spec as the elements of coll? Is this kind of reasoning within the intended scope of clojure.spec?

Alex Miller (Clojure team)11:05:16

That's not a true constraint of conj :)

francesco11:05:36

totally agree on that.

Alex Miller (Clojure team)12:05:14

But you could spec something like that with the :fn spec in fdef which relates the args and ret

Alex Miller (Clojure team)12:05:04

It's passed a map with the conformed value of both

sveri12:05:09

A bit more feedback. I constantly run into errors like this:

In: [0] val: ([{:name "age", :type :int, :null false}]) fails at: [:args] predicate: (cat :cols :de.sveri.clospcrud.schema/columns),  Extra input
:clojure.spec/args  ([{:name "age", :type :int, :null false}])
  clojure.core/ex-info (core.clj:4617)
Most of the times the problem was that I was missing a (s/spec ::columns) expression around the spec definition. Still, everytime I see this error it is so far away from what the problem actually is, that I need time to remember this. Worse is, that at first I look and search if I did something wrong while transforming my schema to spec. Maybe a somewhat better error message is possible?

Alex Miller (Clojure team)14:05:18

Not sure what a generically better error would be

Alex Miller (Clojure team)16:05:38

Interesting - file a jira if you want so we don't lose it over the weekend

sveri17:05:58

@alexmiller: For me it would be something like: "Maybe you forgot a spec around a regex? Try (s/spec ...)" But, I dont know how generic that error is and if it would fit everytime?

sekao18:05:27

is there a way to exclude certain vars from being tested by run-all-tests? i have some side-effecting functions that will cause some havoc if run in generative tests 😃

arohner18:05:28

I’m not entirely clear on when generative testing happens. In some cases (side-effects), I’d like the ability to check conform but not do generative testing

sekao18:05:18

for now i copied the code into my project and added an if statement that checks for :no-check in the var’s metadata

bbrinck19:05:55

@arohner: My understanding is that generative testing only happens if you invoke run-all-tests. If you just wanted to test specific functions in your test suite, you could do something like:

(deftest test-my-function
  (is (= true (:result (t/check-var #’my-function)))))

arohner19:05:15

@bbrinck: I’m pretty sure there are cases aside from that that can trigger it

arohner19:05:25

IIRC, rich’s example from the other day will

arohner19:05:56

repl> (defn foo [fnn] (fnn 42))
#'repl/foo
repl> (s/fdef foo :args (s/cat :f (s/fspec :args (s/cat :i integer?)
                                     :ret integer?)))
repl/foo
repl> (s/instrument 'foo)
#'repl/foo
repl> (foo #(when (even? %) 42))  

ExceptionInfo Call to #'repl/foo did not conform to spec:
In: [0] val: nil fails at: [:args :f :ret] predicate: integer?
:clojure.spec/args  (#object[repl$eval66335$fn__66336 0x55f2e735 "repl$eval66335$fn__66336@55f2e735"])
  clojure.core/ex-info (core.clj:4617)

arohner19:05:11

that ran generative testing on a normal call

arohner19:05:19

which is something I’d like control over

bbrinck19:05:54

I may be misunderstanding, but I don't think it did. I think it just checked that the spec matched the invocation: (foo #(when (even? %) 42))

arohner19:05:14

42 should pass there. The generative example passed zero

bbrinck19:05:25

I think this is just a confusing error message. The value passed is not 0

bbrinck19:05:48

it's #(when (even? %) 42), which returns nil, which violates the spec

arohner19:05:36

(foo #(do (println %) (when (even? %) 42)))
-1
0
-1
0
-1
0
-1

arohner19:05:14

(s/unstrument 'foo)
#'repl/foo
repl> (foo #(do (println %) (when (even? %) 42)))
42
42

bbrinck19:05:11

Hm, yeah, you're right, I was reading that wrong. I still don't think (from playing with other examples) that the generative testing occurs unless you do one of the testing calls. I think this example might just have a bug. I could be misunderstanding though.

bbrinck19:05:49

(I'm trying to try it myself, but on my version of clojure, the fdef is not working for some reason)

arohner19:05:41

which testing call did I run there?

bbrinck19:05:00

Hm, you're absolutely right. I did not expect that.

bbrinck19:05:07

@arohner: Yep, you're correct - there was no testing call, but it's doing some generation for the args for the inner function.

bbrinck19:05:17

@arohner: interestingly, that does not seem to occur in all cases:

(s/fdef double
        :args (s/cat :x integer?)
        :ret integer?)
(defn double [x]
  (if (zero? x)
    "zero"
    (* 2 x)))

(s/instrument 'double)

(double 1)

arohner19:05:38

yeah, I think fspec is the culprit

bbrinck20:05:52

ah, yes, the docs do mention that now that I read them a little more carefully 😄 . Well, TIL

bbrinck20:05:21

Also, FWIW, that example won't quite work on 1.9.0-alpha3. fspec seems to require a ret now

bbrinck20:05:31

nvm, i realized it was a copy/paste fail on my part (didn't grab the full fspec above)

sveri20:05:39

Hi, did anyone try to generate a boolean spec? Like this: (s/def ::required #(instance? Boolean %)). Calling (gen/sample (s/gen ::required)) fails for me with: Exception Unable to construct gen.... This becomes problematic if ::required is part of a nested spec, how would I provide a generator for that?

seancorfield20:05:55

That specific case is covered in the (updated) documentation @sveri

sveri20:05:38

@seancorfield: Ah, thats nice, I just solved it by defining boolean as true / false: (s/def ::boolean #{true false}) for which the generator works too

seancorfield20:05:05

"There are some common cases that currently don’t have standard predicates, but good generators exist. It’s likely there will be changes in this area in the future but for now you might find these useful: (defn boolean? [x] (instance? Boolean x)) (s/def ::boolean (s/with-gen boolean? #(gen/boolean))) (defn uuid? [x] (instance? java.util.UUID x)) (s/def ::uuid (s/with-gen uuid? #(gen/uuid)))"

seancorfield20:05:33

(That didn't paste well -- sorry, on my phone)

sveri20:05:32

Yea, no problem, I get it, thank you 🙂

borkdude21:05:27

Just finished reading the guide. Good stuff.

borkdude21:05:49

Why is (doc subs) printing Spec on the last line. Bug?

user=> (doc subs)
-------------------------
clojure.core/subs
([s start] [s start end])
  Returns the substring of s beginning at start inclusive, and ending
  at end (defaults to length of string), exclusive.
Spec
nil

borkdude21:05:51

What about multi-arity functions like subs, how to write spec for both cases?

borkdude21:05:06

Like this? (s/fdef clojure.core/subs :args (s/or :two-args (s/cat :s string? :start integer?) :three-args (s/cat :s string? :start integer? :end integer?))

borkdude21:05:48

I'm not sure if I should wrap with s/spec, both seem to work: (s/fdef clojure.core/subs :args (s/or :two-args (s/spec (s/cat :s string? :start integer?)) :three-args (s/spec (s/cat :s string? :start integer? :end integer?))))