This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-02-13
Channels
- # adventofcode (35)
- # announcements (2)
- # arachne (1)
- # beginners (71)
- # chestnut (2)
- # cider (100)
- # cljdoc (17)
- # cljs-dev (17)
- # cljsjs (2)
- # cljsrn (2)
- # clojure (53)
- # clojure-austin (2)
- # clojure-europe (1)
- # clojure-finland (2)
- # clojure-italy (3)
- # clojure-nl (7)
- # clojure-russia (56)
- # clojure-spec (56)
- # clojure-uk (35)
- # clojurescript (58)
- # community-development (14)
- # core-async (9)
- # cursive (22)
- # data-science (5)
- # datomic (14)
- # duct (5)
- # emacs (2)
- # expound (4)
- # figwheel-main (6)
- # fulcro (23)
- # kaocha (8)
- # lumo (7)
- # off-topic (10)
- # pathom (6)
- # re-frame (17)
- # reitit (31)
- # ring (3)
- # rum (1)
- # shadow-cljs (45)
- # spacemacs (10)
- # sql (12)
- # testing (9)
- # tools-deps (130)
Is there a way to create an anonymous spec? So instead of defining it, I can just create one and pass it into spec functions?
@caleb.macdonaldblack I suspect the answer is "yes" but it's different between spec1 and spec2...
spec2 as in the next version of spec?
In spec1, you can mostly use a predicate interchangeably with a spec. In spec2, you can construct spec objects on the fly.
Yes, clojure.spec-alpha2
Well I'm now excited for spec2
Hahaha... well, there are no releases yet, but if you're using deps.edn
, you can test against it.
I'm using leiningen. I may as well wait until an official release. I'm glad to see spec moving in that direction
Have you seen Rich's talk from Conj? (Maybe Not)
He talks about future direction for spec...
"Maybe Not"?
if so I haven't seen it. I'll have a look though
Specifically he talks about how s/keys
complects the shape of data and the actual checks that are needed in any given context.
interesting
in both spec 1 and 2 you can create a spec object and pass it to any of the spec api functions
with the caveat that s/keys relies on having registered key specs to rely upon
there may be more support in spec 2 for map selection specs with anonymous key specs
I am working in that area right now
Ah okay i didnt know that
Hi here! I'm playing with clojure specs, I'm struggling in trying to use generators for a spec with regex:
(s/def ::entity (s/and string? #(re-matches #"DEST:\d+")))
Am I wrong in defining something? Or the regex can't be generated by the default lib?
@manuelrascioni you’re missing a %
:
user=> (s/def ::entity (s/and string? #(re-matches #"DEST:\d+" %)))
:user/entity
user=> (s/valid? ::entity "DEST:1")
true
Your spec will generate, but the likelihood that it generates strings that will satisfy the predicate is extremely small. You probably want to provide a generator with the spec, using s/with-gen
@borkdude ah...thank you! It seems I still have to train my eye to check for this kind of mistakes...I will check the docs for the with-gen
thank you for the hint!
@manuelrascioni E.g.:
(s/def ::entity (s/with-gen (s/and string? #(re-matches #"DEST:\d+" %)) #(gen/fmap (fn [i] (str "DEST:" i)) (s/gen nat-int?))))
(gen/sample (s/gen ::entity))
("DEST:1" "DEST:0" "DEST:1" "DEST:2" "DEST:2" "DEST:7" "DEST:16" "DEST:41" "DEST:1" "DEST:4")
just to check if I understood well, the fmap
is used to "customize" the value generated by a generator, and return a generator, right?
fmap returns a new generator which transforms the values generated by the mapped-over generator using a function
a newer better version of that is in test.chuck
I recently ran orchestra which tests ret-specs with speculative on a body of code. The only ret spec which wasn’t correct was for an fdef for which I had not used generative testing.
it was e.g. re-find
, re-matches
, etc. for which I had not considered that it could also return nils, as in
(re-find #"(a)?(b)" "b")
["b" nil "b"]
I wonder how I could have written a generator for this.I would have to generate regexes and strings that would sometimes match, sometimes not
This fun experiment is able to generate regexes that seem to not terminate when executed…
(defn test-re-find []
(let [regex-gen (gen/fmap (fn [parts]
(let [s (str/join parts)]
(re-pattern (str/join parts))))
(s/gen (s/* (s/cat :part
(s/or :string string?
:group (s/with-gen string?
#(gen/fmap (fn [s]
(str "(" s ")"))
(s/gen string?))))
:maybe (s/? (s/with-gen string?
#(gen/fmap (fn [s]
(str s "?"))
(s/gen string?))))))))
matcher-gen (gen/fmap (fn [[r s]]
(re-matcher r s))
(gen/tuple regex-gen (s/gen string?)))]
(map re-find (gen/sample matcher-gen 100))))
user=> (gen/sample regex-gen)
(#"" #"" #"" #"()15?" #"(2)" #"(3)" #"()9?F?JWwff1" #"xE7(re9)(79W)26E?()jKqXKk5?(DY1Xa)()(4m)2qNS3?" #"gW7GAJ9p22Z4o4eWJ?" #"(Gi8r)RQ22uBC?(jj0PolFmd)h7?Taz()(7)GwE0")
I’m trying this now:
(s/def ::regex.char #{"a" "b"})
(s/def ::regex.group (s/with-gen string?
#(gen/fmap (fn [s]
(str "(" s ")"))
(s/gen ::regex.pattern))))
(s/def ::regex.maybe (s/? (s/with-gen string?
#(gen/fmap (fn [s]
(str s "?"))
(s/gen ::regex.pattern)))))
(s/def ::regex.pattern (s/* (s/or :char ::regex.char
:group ::regex.group
:maybe ::regex.maybe)))
This gives me a stackoverflow…
(binding [s/*recursion-limit* 1]
(gen/sample (s/gen ::regex.pattern)))
How do I get generators to play nice with conformers?
(s/def ::my-spec (s/and int? (s/conformer str)))
(gen/sample (s/gen ::my-spec)) ;; => (0 -1 0 0 0 0 6 42 -1 -3) <- want strings here
As written, your spec accepts (only) numbers -- and it is generating numbers that your spec accepts. That's the correct behavior.
Conforming numbers to strings as part of a spec feels very wrong to me (and you know what an advocate I am for certain types of coercion in specs! 🙂)
the thing I was doing for real was the regex.pattern above, where I want to generate strings, but describe those strings in terms of spec
Generators must produce values that are acceptable to your spec.
(and we've had repeated cautions from @alexmiller not to use spec regex for string parsing/generation stuff 🙂 )
for parsing yes, because of performance, there are better tools, but for generation, I currently don’t know a better tool 😛
Why not use test.chuck
s regex string generator?
(or did I miss your rationale for not using that?)
once I have that, I can use test.chuck to generate strings from the generated regexes. and then I can use stest/check to test re-find, etc.
why not make a regex for regexes?
then use test.chuck on it
regex language cannot be expressed with a regex, I think you need a CFG tool like spec
this kinda works:
(s/def ::regex.pattern
(s/* (s/cat :pattern
(s/alt :char #{\a \b}
:group (s/cat :open-paren #{\(}
:inner-pattern ::regex.pattern
:closing-paren #{\)}))
:maybe (s/? #{\?}))))
(s/valid? ::regex.pattern (seq "(ab)"))
(s/valid? ::regex.pattern (seq "ab(ab)?"))
(map str/join (binding [s/*recursion-limit* 2]
(gen/sample (s/gen ::regex.pattern))))
(defn test-re-find []
(let [regex-gen (gen/fmap (fn [r]
(re-pattern (str/join r)))
(s/gen ::regex.pattern))
matcher-gen (gen/fmap (fn [[r strs]]
(re-matcher r (str/join strs)))
(gen/tuple regex-gen (s/gen (s/* #{"a" "b"}))))]
(let [matchers (gen/sample matcher-gen)]
(map re-find matchers))))
(test-re-find)
At least I’m now finding return values that I didn’t account for in an early version of the spec, e.g.:
["ba" "a" "a" "" "" nil nil "" "" ""]