Fork me on GitHub
#clojure-spec
<
2019-11-21
>
Daouda11:11:55

Hey folks, can you help me to understand why I can't gen/generateor gen/sample:

(def email-regex #"^[a-zA-Z0-9._%+-][email protected][a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$")

(spec/def ::email-type (spec/and string? #(re-matches email-regex %)))
(spec/def ::uuid int?)
(spec/def ::first-name string?)
(spec/def ::last-name string?)
(spec/def ::email ::email-type)
(spec/def ::phone #"^\d{12}$")

(spec/def ::person (spec/keys :req [::first-name ::last-name ::email]
                              :opt [::phone]))
(gen/generate (spec/gen ::person))

Error: 
 Show: Project-Only All 
  Hide: Clojure Java REPL Tooling Duplicates  (13 frames hidden)

1. Unhandled clojure.lang.ExceptionInfo
   Couldn't satisfy such-that predicate after 100 tries.
   {:pred #function[clojure.spec.alpha/gensub/fn--1876],
    :gen
    {:gen #function[clojure.test.check.generators/such-that/fn--40141]},
    :max-tries 100}

guy11:11:31

Have you tried exercising your specs?

guy11:11:36

Couldn't satisfy such-that predicate after 100 tries

guy11:11:41

Thats the key part to the error message

guy11:11:54

I would just go through each def and see if they can produce a result

vlaaad11:11:14

my bet is on email and phone

guy11:11:20

yes i would agree

guy11:11:51

If the predicate is too hard to er fufill? based on random string input, don't you usually just create a generator for those complicated predicates?

guy11:11:54

also i'm not all that sure how (spec/def ::phone #"^\d{12}$") works

guy11:11:17

If you exercised ::phone what would you get? thats a pattern isnt it :thinking_face:

guy11:11:06

Unable to construct gen at: []

guy11:11:11

Thats what i get

guy11:11:18

for (s/exercise ::phone)

Daouda11:11:10

Just saw now, the answers, will be right back after doing what you said 😄

guy11:11:30

So then I think for the email, its just as simple as, the string generator can't create a random entry that matches your regex

guy11:11:50

which makes sense right? as even after 100 times its going to be difficult to randomly create an email i think haha

Daouda11:11:26

After trying to exercise each attribute, it appears that ::phone and ::email-type can't be exercised

guy11:11:08

ahh thanks that was the link i was looking for. :thumbsup:

seancorfield17:11:37

https://github.com/gfredericks/test.chuck has a generator for regex strings that I've found very useful.

sgerguri22:11:19

I am trialling offloading some extra logic onto s/or and would like some collective opinion, if y'all were so kind. Suppose I have a spec defined like this:

(s/or :x :x/item
      :y :y/item)
Now suppose I add x1 and want to combine it with x at some point; so let's say I start making the semantic distinction between what this thing is and what it can be combined into:
(s/or [:additive :x] :x/item
      [:additive :x1] :x1/item
      [:multiplicative :y] :y/item)
Now, suppose further that I want to encode the order in which the respective operation should be defined. Now I'm thinking it would be really useful to be a bit more descriptive in my tags, so why not use a map?
(s/or {:type :additive
       :operand-type :x
       :precedence 1}
      :x/item

      {:type :additive
       :operand-type :x1
       :precedence 2}
      :x1/item

      {:type :multiplicative
       :operand-type :y}
      :y/item)
Now I'm thinking, should I simply add this via a conformer, keeping the tags simple (and thus transforming them at a higher level elsewhere in the spec) or do I keep this? This might all seem like a contrived example - and it is, because my use case isn't algebraic operations, but rather data merging, and thus it's really important to define what order each data types should be merged in, and what the parent type is, so that the correct merge strategy is applied. My question is - am I really using s/or in a way I shouldn't be or would you still consider this ok?