Fork me on GitHub
#clojure-spec
<
2016-10-11
>
danielstockton08:10:21

I'm calling explain on a spec and it's returning nil, why would that be?

danielstockton08:10:57

answer: i need to use explain-str, explain just prints to out

dm309:10:08

how would you create a spec for the following: 1) ns store - defines ::state as (s/keys :req [::data ::metadata]) where ::data is a map? 2) ns x produces a ::state with concrete ::data How would the ::x-state spec be defined? (s/and ::store/state #(s/assert ::x-data (::store/data %))) doesn't look right...

danielstockton09:10:53

On it's own, [1 1] conforms to the ::pattern spec

samedhi14:10:10

(ns example.paths
  (:require
   [cljs.spec :as s]
   [cljs.spec.impl.gen :as gen]
   [cljs.spec.test :as test]))

(s/def ::keyword-or-string
  (s/with-gen
    (s/or :keyword keyword?
          :string string?)
    #(s/gen #{"a" "b" :c :d :e/z})))

(def path (s/coll-of ::keyword-or-string
                     :min-count 1
                     :kind vector?))

(s/def ::path
  (s/with-gen
    path
    (fn []
      (s/gen
       (gen/such-that
        #(-> % count (< 5))
        (s/gen path))))))

samedhi14:10:54

#object[Error Error: Unable to construct gen at: [] for: [object Object]]
Error: Unable to construct gen at: [] for: [object Object]

samedhi14:10:42

So I am trying to reduce the size of generated ::paths to at most be of length 5. It isn’t part of the actual spec for a ::path, but it is something I would like when generating, any idea what I am doing wrong?

jrheard15:10:33

@samedhi there is a 20% chance that what i’m about to say is relevant, but i’ll say it just in case

jrheard15:10:51

i’ve run into trouble with s/coll-of specs that define a :kind but not an :into

jrheard15:10:57

try adding :into [] and see if it does anything

jrheard15:10:02

it’s probably irrelevant, but who knows

jrheard15:10:30

this is definitely a thing if your :kind is set?, you need to define :into #{} for generators to work afaict

samedhi15:10:57

(ns example.paths (:require [cljs.spec :as s] [cljs.spec.impl.gen :as gen] [cljs.spec.test :as test]))

(s/def ::keyword-or-string
  (s/with-gen
    (s/or :keyword keyword?
          :string string?)
    #(s/gen #{"a" "b" :c :d :e/z})))

(gen/generate (s/gen ::keyword-or-string)) => :d

(def path (s/coll-of ::keyword-or-string
                     :min-count 1
                     :kind vector?
                     :into []))

(gen/generate (s/gen path)) => [“a” :c “b” “b” “b” “b” “a”]

(s/gen
 (gen/such-that
  #(-> % count (< 5))
  (s/gen path)))
=> (errors with)
#object[Error Error: Unable to construct gen at: [] for: [object Object]]
Error: Unable to construct gen at: [] for: [object Object]
...

samedhi15:10:42

@jrheard is this what you mean? In this case I am trying to return a vector of keywords or strings, so I set :kind to vector? and :into to []

jrheard15:10:44

yeah, that’s what i mean; looks like it wasn’t relevant, sorry 🙂

samedhi15:10:42

Hey, no problem, I appreciate it.

thegeez15:10:28

@samedhi should that be gen/generate instead of s/gen around the gen/such-that?

samedhi15:10:47

@thegeez, actually, that does work… Let me play a bit with it.

samedhi15:10:32

On my first post, 2 paste up, I had.

samedhi15:10:40

(s/def ::path
  (s/with-gen
    path
    (fn []
      (s/gen
       (gen/such-that
        #(-> % count (< 5))
        (s/gen path))))))

samedhi15:10:01

I think s/with-gen requires a generator as the second argument...

samedhi15:10:37

I think I am basing it off of this from http://clojure.org/guides/spec#_explain

samedhi15:10:51

(s/def ::kws (s/with-gen (s/and keyword? #(= (namespace %) "my.domain"))
               #(s/gen #{:my.domain/name :my.domain/occupation :my.domain/id})))

samedhi15:10:11

> Note that with-gen (and other places that take a custom generator) take a no-arg function that returns the generator, allowing it to be lazily realized.

samedhi15:10:03

So it sounds like I want a zero-args function that returns a generator.

samedhi15:10:29

Kind of seems like that is what I have above… right?

samedhi15:10:37

Maybe gen/such-that is already a generator or maybe it is a spec?

thegeez15:10:56

remove the s/gen around the gen/such-that

thegeez15:10:42

gen/such-that is already a generator indeed

samedhi15:10:45

(s/def ::path
  (s/with-gen
    path
    (fn [] (gen/such-that #(-> % count (< 5)) (s/gen path)))))

samedhi15:10:06

Thanks, that helped me out a lot, data seemed to just take forever on generating without this. (though that may actually be my formater and not the actual generation). Fixed loading issues.

samedhi17:10:37

(s/def ::path
  (s/with-gen
    path
    (fn [] (gen/fmap #(vec (take 5 %)) (s/gen path)))))

samedhi17:10:06

Is actually slightly better, as it never fails to generate under the 100 try limit.

lvh17:10:40

@alexmiller Suggestion: a special s/cat, primarily intended for specifying args in fdef, that automatically calls s/spec on each of its arguments

lvh17:10:58

@alexmiller I feel like a lot of people have bumped their toe on the nested regex gotcha

alexmiller17:10:07

I think that’s unlikely something we would add

alexmiller17:10:00

and in general, I don’t find that that is (usually) what you want

alexmiller17:10:52

it seems more common from specs I’ve written to have s/cat of s/coll-of for functions. For macros, you sometimes have this but I find it’s really important in that case to develop the awareness of when you are really needing to drop nested levels - that’s critical to build reusable parts, and often for making workable recursive specs

danielstockton18:10:57

I think just making this clearer in the guides, in the specing functions section, would help

danielstockton18:10:15

I found where it's mentioned now, but not when I was trying to work out what was wrong

danielstockton18:10:16

You could argue I should have read the guide more thoroughly

bhagany21:10:19

for me, it was just forgetting that cat is a regex operator. I didn’t feel like the guide could have been much clearer about it.