Fork me on GitHub
#clojure-spec
<
2017-06-30
>
mpenet08:06:23

@alexmiller I got the ball moving here: https://dev.clojure.org/jira/browse/CLJ-2194. Looking forward to seeing what comes out of this.

danielstockton08:06:48

I have the following om.next style spec:

(s/def ::ident (s/and vector? (s/cat :ident keyword? :value #(not (coll? %)))))
(s/def ::join-key (s/or :prop keyword? :ident ::ident))
(s/def ::join (s/and (s/map-of ::join-key ::query) #(= (count %) 1)))
(s/def ::union (s/and (s/map-of keyword? ::query) #(> (count %) 1)))

(s/def ::param-expr
  (s/cat :query-expr ::query-expr
         :params map?))

(s/def ::mutation-expr
  (s/or :no-params (s/cat :mutate-key symbol?)
        :with-params (s/cat :mutate-key symbol?
                            :params map?)))

(s/def ::query-expr
  (s/or :prop keyword?
        :ident ::ident
        :mutation-expr ::mutation-expr
        :union ::union
        :join  ::join
        :param-expr ::param-expr))

(s/def ::query
  (s/or :recursion (s/or :depth number?
                         :unbounded #(= % '...))
        :query     (s/and vector?
                          (s/+ ::query-expr))))

(println (s/conform ::query [:one :two :three]))
(println (s/unform ::query (s/conform ::query [:one :two :three])))
Why does the first print give [:query [[:prop :one] [:prop :two] [:prop :three]]] but the second give (:one :two :three) (sequence not a vector)?

danielstockton08:06:49

If query is spec'ed as being a vector? shouldn't unform also return a vector?

jwkoelewijn10:06:41

hi there, I’m having some troubles regarding circular refs in my specs. I have a spec defining an office which has employees in the office ns, and I have an employee (in an employee ns), which should have an office. As you cn guess, this results in circular dependencies (or when I don’t require one of the namespaces, it results in Unable to resolve spec. Is there any recommended method to deal with issues like this?

alexmiller13:06:00

@jwkoelewijn code would help. What you're describing should be possible

rickmoynihan14:06:37

Does anyone know of any ring middleware that pretty prints ex-info‘s that raise spec explain-data? Thinking of something like prone for dev but for spec problems.

rickmoynihan14:06:09

prone currently gives me a huge wall of text

bbrinck14:06:11

Has anyone worked on a partial spec for specs? Or a test.check generator? I’m mostly interested in generating specs rather than conformance or validation

alexmiller14:06:50

bbrinck: https://dev.clojure.org/jira/browse/CLJ-2112 is about specs for specs. Those also are a step towards spec generators which I’ve also done some more work that’s not public yet.

bbrinck15:06:36

Excellent news. I’m voting for that one now 😉

bbrinck15:06:15

I may work on some generators soon. I’m guessing it will be a subset of what you’ve done, but it’ll be public, so I’m happy to share in case it’s useful.

alexmiller15:06:38

I’ve in particular worked on the generators in the direction of testing spec itself. Namely, if you have spec specs with working generators, you can generate specs, then use those to generate data, then validate that the generated values are valid according to the generated specs

alexmiller15:06:47

the last time I worked on this I mostly convinced myself there was a bug in multi-spec gen for the non-keyword case that was giving me grief, but I never ran it down

eraserhd18:06:38

I wonder how you spec that s/conform returns :clojure.spec/invalid as a valid result.

bbrinck14:06:30

@rickmoynihan This isn’t directly applicable, but data-frisk includes some nice-looking spec messages. I wonder if that could be ported.

bbrinck14:06:01

Do you need to return explain-data or could your raise an ex-info that contains explain (or some other string representation of the errors)?

bbrinck14:06:20

I’m just wondering if maybe you could solve the issue by formatting the errors at the source. That might not be desirable though.

danielstockton14:06:23

Can I destructure metadata? For example, if I have a vector [1 2 3] which has meta {:some :meta} -> [:vector [1 2 3] :meta {:some :meta}]. I can't work out how I'd spec this.

mpenet14:06:24

@danielstockton with a predicate I guess

danielstockton14:06:37

@mpenet Tried that but I couldn't get destructuring as above when conforming. My understanding of spec is still fairly superficial.

mpenet14:06:05

you can do this with a custom conformer I think

danielstockton14:06:53

Ok, I'll look into that, thanks.

danielstockton14:06:27

I don't need the conformed output exactly as above but I need to be able to easily identify the two parts.

mpenet14:06:48

(s/conform (s/and vector? (s/conformer #(hash-map :vector % :meta (meta %))))) (with-meta {} {:foo 1}) -> {:vector [] :meta {:foo 1}}. this example conformer is a bit broken but that's the idea for the coercion part

danielstockton14:06:20

Cool, why do you say it's broken?

mpenet14:06:24

it's ok actually, before the edit it was 🙂

danielstockton14:06:27

Am I right that conformer takes a second argument to unform? This might address the issue I had above with unforming to a seq.

mpenet14:06:09

never used unform but yes, it might work

danielstockton14:06:10

Still curious to know exactly why it unforms to a seq. I noticed for simpler specs (just vector?) it unforms correctly so it must be a side effects of something.

mpenet14:06:16

I think that's a bug

danielstockton14:06:56

Should I be calling it a vector in the conformed output or is it a 'tuple'?

danielstockton14:06:50

If it is a bug, I can't work out where it comes from.

wilkerlucio14:06:21

@danielstockton for that case I think a tuple makes more sense than a cat, a cat is for more regexy stuff

wilkerlucio14:06:17

I think it unforms to a seq because you are using a cat, which is for regexy stuff, and I believe it will always conform to a seq

danielstockton14:06:42

@wilkerlucio Which cat are you referring to?

danielstockton14:06:01

The only ones I see are deeper in the spec

wilkerlucio14:06:15

this one: (s/def ::ident (s/and vector? (s/cat :ident keyword? :value #(not (coll? %)))))

wilkerlucio14:06:19

or I'm looking at the wrong place?

danielstockton14:06:30

I don't think the query I'm unforming has any idents: [:query [[:prop :one] [:prop :two] [:prop :three]]]. The [:prop :one] is the conformed value for a :prop ::query-expr which is just a keyword?

danielstockton14:06:55

The original query is just [:one :two :three]

danielstockton14:06:48

I did think it might be related to a cat or similar, somewhere, but I couldn't explain it.

wilkerlucio14:06:00

ah, I think I was reading it wrongly, sorry, so it seems something is not aligned between conform and unform?

danielstockton14:06:04

I mean, perhaps I just misunderstand unform because it doesn't promise to give back exactly what you put it, only undo the destructuring..

danielstockton14:06:29

I'm just curious exactly what's causing this behaviour.

alexmiller14:06:36

it’s unclear to what is not working as you expect from the back chat - can you restate?

danielstockton14:06:23

Why does the first print give [:query [[:prop :one] [:prop :two] [:prop :three]]] but the second give (:one :two :three) (a lazy sequence, not a vector)?

alexmiller15:06:13

looks like this is sufficient repro:

(s/def ::query (s/and vector? (s/+ keyword?)))
(println (s/conform ::query [:one :two :three]))
(println (s/unform ::query (s/conform ::query [:one :two :three])))

danielstockton15:06:17

Yep thanks, I made it look unnecessarily complicated.

alexmiller15:06:18

so in conform, the general approach is to avoid rebuilding a collection if at all possible, so there you just end up with the original vector on conform

alexmiller15:06:46

unform of a regex spec however will always (I think) yield a seq

alexmiller15:06:17

unform of an s/and walks the preds in reverse

danielstockton15:06:02

Make sense, so it's the s/+ specifically. And if I wanted unform to yield the same collection type, I'd use a customer conformer with a second argument? I'm trying to use spec to implement a parser and I want to be able to go from query <-> ast. I don't know if this is a good use case for spec or not.

alexmiller15:06:20

the broader problem here is: how do I use a regex spec but constrain to vectors and this is a known problem area that Rich and I have discussed at length as it came up a lot in spec’ing the core macros

alexmiller15:06:02

there is currently no simple solution that works as you’d like for all of conforming, generation, explain, and unconform (although you can make subsets of those work)

alexmiller15:06:58

I’m trying to work out if in this case you could actually somehow supply a custom unform function to the vector? pred as that’s not something I’ve tried

alexmiller15:06:32

looks like there is no easy way to do so although there is certainly the potential to do so

danielstockton15:06:09

Ok, I'll keep my ear to the ground.

eraserhd18:06:27

Can i recursively override a generator? e.g. pass a function to s/spec's :gen that overrides just the recursive part, and close over such this also overrides the recursive part, making a depth-limited generator?

eraserhd18:06:56

I've done this from scratch with clojure.test.check.generators, just trying to see if I can reuse most of what spec would have done.

alexmiller22:06:35

Recursive generators are already depth limited based on the dynamic var in spec

alexmiller22:06:46

In case that solves the problem