Fork me on GitHub
#clojure-spec2016-05-25
>
sveri12:05:29

Is there something in spec that is comparable to string? but for a boolean value?

sveri12:05:48

except #(instance? Boolean %)

richhickey12:05:14

@sveri: not yet. Obviously there are several more useful predicates that could go into core. Considering which ones now

sveri12:05:50

@richhickey: Yea, it would be nice to be consistent, much less work for the brain then 🙂

moxaj12:05:49

(spec/def ::foo (spec/cat :kw keyword? :int integer?))
(spec/def ::bar (spec/coll-of ::foo []))
(spec/explain ::bar [[:a 10] [:b 20] [:c "30"]])
;; => val: [[:a 10] [:b 20] [:c "30"]] fails predicate: (coll-checker :spec/foo)
Why doesn't the error reporting go into detail (like: val "30" fails predicate: integer?) Is this intentional?

sveri12:05:17

Hm, is it ok to use ::spec/name for my own specs? Also I have not found the source for ::spec/string, neither by searching github (which is a bit harder, because it ignores "::", nor by looking at the api.

sveri12:05:36

Also, is it correct that ::spec/name has string? as predicate?

kingoftheknoll12:05:24

@alexmiller: About a week ago you hinted on Reddit at improved error messages for Clojure. Don’t think anyone’s said that here but it seems like we just got a peek into how that might be accomplished if all of Clojure.core implements specs.

mjhamrick14:05:08

What's the best way to have fdefs be checked during lein test? Instrument all works at the repl, but seems to not during test.

anmonteiro14:05:12

@mjhamrick: what I’ve done is (clojure.spec.test/check-var #’fn-speced-with-fdef)

mjhamrick14:05:49

@anmonteiro: are you just putting that in each test?

anmonteiro14:05:10

the project where I tried it was pretty small

sveri14:05:58

@mjhamrick: I did put (s/instrument-all) at the top of each test namespace

Alex Miller (Clojure team)14:05:44

@kingoftheknoll: yes, I have done additional work in this area

Alex Miller (Clojure team)14:05:55

@sveri ::spec/name and ::spec/string don't exist - what is that from?

sveri14:05:37

@alexmiller: Thats why I have not found it in the code. Not sure where exactly it came from, its just that cursive offered it via code completion. Maybe I typed it in a different namespace or whatever. Anyway, good to know.

michaeldrogalis14:05:28

@richhickey @alexmiller: I have another proposed enhancement to explain-data. Several predicates, such as keyword?, or integer? are programmatically easily recognizable via :pred for non-conforming values. It's a straightforward equality check (e.g. (= 'keyword? (:pred error-map))). Other predicates are, to my understanding, more difficult to recognize. Take s/keys and s/tuple for example. In the former, keys yields a predicate when a required key is missing. While the predicate is somewhat understandable when you eye ball it, picking it out programmatically has been error-prone. The same goes for s/tuple when the number of arguments is incorrect.

Alex Miller (Clojure team)14:05:13

an example would help make this specific

michaeldrogalis14:05:16

Those functions have privileged access to which predicates are failing. It might be helpful to provide named predicates in those cases so that consumers of explain-data could be more intelligent. See - https://gist.github.com/MichaelDrogalis/016ac62cf3f1e899fb89fd79f2de2277

Alex Miller (Clojure team)14:05:02

thx for the example! :)

Alex Miller (Clojure team)14:05:49

oh the contains stuff that's inside keys

Alex Miller (Clojure team)14:05:25

I'll have to defer that to Rich, I think he's not around atm

moxaj14:05:39

It seems to me that inner conformed values are lost when using coll-of or map-of. Example:

(spec/conform (spec/coll-of (spec/and integer?
                                      (spec/conformer (constantly nil)))
                            [])
              [1 2 3])
returns [1 2 3] instead of [nil nil nil]. Meanwhile, cat and keys preserves them. How come?

michaeldrogalis14:05:14

@alexmiller: Sure 🙂 Ill be back later.

Alex Miller (Clojure team)14:05:00

@moxaj Rich said above "currently conform doesn't flow into coll-of, so the value is never conformed, only checked" which I think is likely related to this

moxaj14:05:20

@alexmiller alright, thanks!

sveri15:05:00

I am converting a lib from schema to spec and in schema I was able to inline define the return values / args. Is there a way to do that in spec too? The alternative in this case is a lot more code.

Alex Miller (Clojure team)15:05:08

for map keys, no - that's the whole point of the map / attributes split

Alex Miller (Clojure team)15:05:24

the benefit is that you are creating named reusable semantics for ::entityname, ::ns, etc etc

Alex Miller (Clojure team)15:05:57

so you can use those elsewhere too

sveri15:05:50

@alexmiller: Yea, I understand the benefits, just wanted to make sure I did not miss anything

mpenet15:05:19

Is it possible to hook ones own error message if a predicate fails? Ex we have schemas that takes a string that should be a valid parse of our internal query language, and if fails should return a nicely structured error with line/col num and a human readable message. With prismatic Schema it is possible to do with our own schema type extending a protocol for explain/spec.

mpenet15:05:37

Correct me if I am wrong, but I think so far I can only get a generic "predicate foo? failed" type of message

Alex Miller (Clojure team)15:05:55

there is not currently a way to provide a custom error for a failing predicate

Alex Miller (Clojure team)15:05:00

you can use explain-data to detect problems and produce a custom error message

Alex Miller (Clojure team)15:05:31

but @richhickey can comment on whether that might be something we could do

mpenet15:05:30

that's what I suspected. Something like ex-info for specs could be useful

Alex Miller (Clojure team)16:05:40

why not explain-data for that?

Alex Miller (Clojure team)16:05:55

or maybe I misunderstand the suggestion

mpenet16:05:59

I actually missed it, that could work yes

Alex Miller (Clojure team)16:05:39

that's what instrument does on a failing function spec

mpenet16:05:11

hmm I am not sure that's really equivalent. I would have to wrap all schemas that contains these values with a special validation fn I think. I would like to have my own datastructure returned by explain-data (or something else) when the predicate fails. I am not sure that's doable with the current approach where predicates return just a potentially truthy value.

mpenet16:05:31

but I could be wrong. I played for 20min with specs so far.

mpenet16:05:07

this might sound like a small concern, but in a larger context it's very useful: we have a whole api around the manipulation of data containing such queries, being able to use the same schema type and framework and not wrap the whole thing because of a single type in a potentially nested Schema is a nice feature, in our case it also integrates for free with frameworks that just follow this Schema format (rest server, swagger etc).

eraserhd17:05:55

First piece of feedback is that clojure.spec makes me really want “boolean?"

sveri17:05:57

@eraserhd: @richhickey said they consider adding this and some more core types (I felt the need for it too :-))

eraserhd17:05:50

I’m still playing with it, but the second thing I’m working through is the repetition, especially for function arguments.

Alex Miller (Clojure team)18:05:19

@eraserhd: we have boolean? in almost every example namespace we wrote. so, agreed. :) definitely near the top of the wanted-predicates list.

eraserhd18:05:51

Another interesting problem I encountered: I used a set for boolean: #{true false}. But sets aren’t treated differently in this context, although I might expect that they are. Namely #(#{true false} false) ;=> false. So having a set in a spec which contains nil or false is awkward.

Alex Miller (Clojure team)18:05:58

it is kind of an interesting wrinkle though that the obvious way to write boolean? is with #(instance? Boolean %)

eraserhd18:05:15

It might at least deserve a call-out in docs.

eraserhd18:05:59

Also, a lot of things seem to leak their internal implementations wrt error messages.

Alex Miller (Clojure team)18:05:31

but Clojure uses (only) canonical boolean values

Alex Miller (Clojure team)18:05:57

@eraserhd: prob better for a mention in the ref doc (which isn't there yet)

eraserhd18:05:55

@alexmiller: I encountered a couple. Just a minute and I’ll paste.

sveri18:05:31

Anyone else seeing problems with reloading test namespaces in the REPL (cursive)? I just commented out some tests, reloaded the test ns in the REPL, hit run-tests, but still, all tests were run. I noticed something similar with test-refresh not recognizing commented fdefs. I cannot point at anything right now, but, there seems to be something broken

kovasb19:05:56

@cfleming: are there gonna be slick Cursive toolips for providing associated specs when editing functions arguments?

kovasb19:05:32

I imagine I'm editing the arg to some function that takes a big map, and as I'm filling in the components of the map its telling me what each piece should conform to

arohner19:05:26

What is the spec for ‘map? i.e. in core.typed, I’d write something like (t/ann map [ [ X -> Y] [X] -> [Y] ). i.e. it takes a collection of X, and a function of X->Y, and returns a collection of Y. Is there a way to do that in spec?

bbrinck19:05:15

I’m missing something related to recursive specs. I want to define a vaguely hiccup-like structure where the first element is the name, followed by some attributes, followed by a vector of children. What am I doing wrong here? https://gist.github.com/bhb/6cfcb3b38757442aec4ba5db46148699

hiredman19:05:41

if you add another (s/spec ....) around the ::tag I get a success, but I am not sure why

hiredman19:05:32

I guess you are in the regex context that matches the outer vector, so you need another s/spec to create a new regex context for the list of child vectors, then you need another s/spec to get out of the regex context of the vector of children and specify an individual child

anmonteiro19:05:21

I’d also be interested to see the spec for clojure.core/map

bbrinck19:05:24

@hiredman: Ah, thanks! I had to read that a few times, but now it makes sense 🙂

sveri19:05:25

Ok, I dont understand how to use instrument. My guess was, that if I put (s/instrument-all) into a namespace all functions are instrumented whenever I load the file into the REPL. But thats not the case, instead I have to execute that function everytime after I loaded a namespace.

sveri19:05:59

Also, it works when I put it to the end of a ns

dryewo20:05:33

Hi all, first I want to thank Rich&Co for this awesome innovation 👍 And I have a question:

(s/def ::even-big-or-small (s/and (s/or :very-big #(> % 1000)
                                        :very-small #(< % 1))
                                  even?))
(s/valid? ::even-big-or-small 10000)
;; throws CompilerException java.lang.IllegalArgumentException: Argument must be an integer: [:very-big 10000]
what am I doing wrong?

dryewo20:05:21

just noticed, works fine the other way around:

(s/def ::even-big-or-small (s/and even?
                                  (s/or :very-big #(> % 1000)
                                        :very-small #(< % 1))))

anmonteiro20:05:06

@dryewo: in the first case, it’ll conform to :very-big and output [:very-big 1000]

anmonteiro20:05:03

(s/def ::even-big-or-small (s/and (s/or :very-big #(> % 1000)
                                               :very-small #(< % 1))
                                        #(even? (second %))))

anmonteiro20:05:39

even? gets [:very-big 1000] so we are interested in the second element

anmonteiro20:05:51

that’s the explanation, but I’m also unsure if it’s expected

dryewo20:05:47

yes, I also got this idea of what’s happening, but is it the correct behavior?

dryewo20:05:13

shouldn’t s/and be order agnostic?

anmonteiro20:05:07

I’ve read somewhere that it’s not, by design

anmonteiro20:05:13

but I can’t recall the reason

dryewo20:05:08

I’m reading the guide: http://clojure.org/guides/spec maybe I should first read it to the end before asking more questions 🙂

dryewo20:05:40

thanks, there is even a section about it in the end of the guide, I’ll look into it

kenny20:05:09

Is there a pattern for whether you spec functions or use spec for validation (pre/post-conditions)? There doesn't seem to be a reason for pre/post-conditions if you spec a function.

sveri20:05:51

I dont understand that, I have the code in the following snippet, and it fails with:

(remove-autoinc-columns [{:foo "some_t"}])
ExceptionInfo Call to #'de.sveri.clospcrud.s-play/remove-autoinc-columns did not conform to spec:
At: [:args] val: ([{:foo "some_t"}]) fails predicate: (cat :cols :de.sveri.clospcrud.s-play/columns),  Extra input
:clojure.spec/args  ([{:foo "some_t"}])
  clojure.core/ex-info (core.clj:4617)

anmonteiro21:05:27

@sveri: you need to wrap ::column in s/spec

anmonteiro21:05:33

like this: (s/def ::columns (s/cat :col (s/* (s/spec ::column))))

Alex Miller (Clojure team)22:05:22

@arohner: @anmonteiro here's a spec for map (assumes a seqable? predicate like the one in core.incubator)

Alex Miller (Clojure team)22:05:26

(s/fdef clojure.core/map
  :args (s/cat :f ifn?
               :colls (s/* seqable?))
  :ret (s/or :seq seqable? :transducer ifn?))

anmonteiro22:05:17

simpler than I imagined

Alex Miller (Clojure team)22:05:18

I've left off :fn because I'm lazy but you could also verify some additional things

Alex Miller (Clojure team)22:05:04

like :f / :transducer or :colls / :seq are the valid combos and even things like whether the :ret cardinality is minimum of the input colls cardinality

Alex Miller (Clojure team)22:05:09

but that's enough to detect things like (map inc 100)

Alex Miller (Clojure team)22:05:47

which is the kind of thing that normally leads to IllegalArgumentException Don't know how to create ISeq from: java.lang.Long

Alex Miller (Clojure team)22:05:27

which, if it's occurring in a nested context can be pretty confusing

tyler22:05:20

Anyone given the test namespace a try yet? I’m trying to figure out how check-var works. It appears to be hanging half the time I try it and passing tests the other half.

arohner22:05:18

@alexmiller: is it possible to check that f’s input matches the coll?

arohner22:05:43

right now I don’t see a way to do that

arohner22:05:08

because there doesnt’ seem to be a way to ‘compare’ specs

Alex Miller (Clojure team)22:05:24

you would have to use a custom predicate to do so

Alex Miller (Clojure team)22:05:17

the :args spec could be (s/and <current> #(custom predicate on the args))

Alex Miller (Clojure team)22:05:04

I guess you might need to describe :f with an fspec to get the right parts to work with

tyler22:05:04

I’ve created a gist with my attempt to try and use check-var it is returning nil, is that expected behavior? https://gist.github.com/tvanhens/28b7f744d799910750d8ae3942680997

hiredman23:05:21

do you have the test.check library on the classpath?

tyler23:05:22

Yup gen/generate works

stuarthalloway23:05:57

@tyler: there is a bug there, I hit it too

stuarthalloway23:05:56

I think it is fixed on master

tyler23:05:56

Ah awesome thanks

eraserhd23:05:15

@alexmiller An example wrt errors: (spec/explain (spec/tuple number?) []) gives an error about #(clojure.core/= (clojure.core/count %) 2).

richhickey23:05:52

@eraserhd: "val: [] fails spec: _ predicate: (clojure.core/= (clojure.core/count %) 1)” says the collection should have count == 1