This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-10-03
Channels
- # aws (1)
- # bangalore-clj (3)
- # beginners (3)
- # boot (9)
- # business (1)
- # cljs-dev (72)
- # cljsjs (7)
- # clojure (86)
- # clojure-austin (1)
- # clojure-belgium (4)
- # clojure-brasil (14)
- # clojure-conj (3)
- # clojure-dev (10)
- # clojure-italy (4)
- # clojure-poland (14)
- # clojure-russia (36)
- # clojure-spec (144)
- # clojure-uk (50)
- # clojurebridge (1)
- # clojurescript (160)
- # clr (2)
- # core-async (8)
- # cursive (56)
- # datomic (34)
- # devcards (3)
- # emacs (2)
- # ethereum (1)
- # events (3)
- # hoplon (21)
- # jobs (2)
- # leiningen (9)
- # luminus (3)
- # off-topic (1)
- # om (26)
- # onyx (42)
- # pedestal (29)
- # protorepl (1)
- # re-frame (43)
- # reagent (26)
- # rethinkdb (4)
- # ring-swagger (4)
- # spacemacs (5)
- # specter (4)
- # untangled (102)
- # vim (43)
- # yada (10)
that sounds pretty weird
i think tomorrow i’m just gonna have to learn how to use checkout dependencies and start adding printfs to spec.test code so i can see what’s going on in there
@jrheard fwiw, I’m eager to hear what you learn. I tried getting doo to run my cljs spec tests a few weeks ago, and hit a wall.
https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/spec/test.cljc#L210 says that options maps passed to (s/check) should look like {:clojure.spec.test.check/opts {:num-tests 100}}
i haven’t managed to reproduce this [] return value issue using my local checkout of clojurescript, so maybe this behavior has been fixed on master? will poke at it some more in the morning
Are you sure about the options map @jrheard ? When I was trying that on Clojure, it definitely needed to be :clojure.spec.test.check/opts
(even tho’ no such namespace exists).
If it really is the latter, as you say, then that’s a bug in my opinion — ClojureScript should follow Clojure there I think?
How to override keys in (s/merge ..)
? My failing attempt:
(s/def :my-ns/a string?)
(s/def :my-other-ns/a int?)
(gen/generate (s/gen (s/merge (s/keys :req-un [:my-ns/a])
(s/keys :req-un [:my-other-ns/a]))))
=> ExceptionInfo Couldn't satisfy such-that predicate after 100 tries. clojure.core/ex-info (core.clj:4725)
@decoursin: what do you expect merged spec to be?
Takes map-validating specs (e.g. 'keys' specs) and
returns a spec that returns a conformed map satisfying all of the
specs. Unlike 'and', merge can generate maps satisfying the
union of the predicates.
does not sound like it can satisfy int?
and string?
with a same value on a single keyI think in terms of usefulness the way it's behaving atm is probably best, though I could see an argument for it behaving like core.merge
Yeah I think it should behave like core.merge. I'd rather see that what @misha described to be called like union or something
is there a way to validate against a (dynamic) subset of keys in a map? ex I define an exhaustive spec for a map (with mixed req/opt), and at runtime I want to be able to validate against a subset of it
ex with Schema you can just do (since map specs are just Maps) :
(defn validate-subset
[schema value]
(-> (select-keys schema (keys value))
(s/validate value)))
seems like s/select-keys
could be a nice addition actually, thoughts @alexmiller ?
given spec's "global" nature, I'd go and name all the combos.
also multi-spec
might be a good fit too
it's a common enough problem imho, any REST api would hit it for partial update for instance
the least horrible (yet it sucks) imho would be to call valid? against every key in the set but yuk. Or just have a s/keys
spec with all as optional for updates, but that's a lot of duplication I'd like to avoid
then probably I'd have 20 different specs defined, and constructed map spec on demand based on incoming map's keys
20 for possible keys, + maybe some for key combinations, like "if that one present, those 2 are required".
a collection of specs for possible keys should not necessarily be a ready to use map spec.
not going to do that, that's truly awful, not to mention for 20 fields (the example here but some of our records have more than this) that's a large number of specs (a lot more than 20)
@mpenet how about this?
(s/def :foo/bar int?)
(s/def :foo/baz string?)
(def schema #{:foo/bar :foo/baz})
(defn validate-subset
[schema m]
(let [ks (filterv schema (keys m))]
(s/valid? (s/keys :req `[~@ks]) m)))
(validate-subset schema {:foo/bar 1 :foo/baz "y"})
=> true
(validate-subset schema {:foo/baz "y"})
=> true
(validate-subset schema {:foo/baz 1})
=> false
That's more or less what I mentioned earlier, but I dont like it. Having 1 separate spec with all opt keys is probably nicer
Is there a way to instrument everything at once, rather than individual functions?
Just call instrument with no args
Is there a good way to hook this into the test runner? I imagine it can be quite helpful to know not only what tests failed but which functions got unexpected data.
I suppose, i'd have to do it in every namespace
Might also be useful to turn it on whenever starting lein repl or in a dev environment
@alexmiller (s/valid? #{false} false) => false
that's what you mention on github?
it does look like a bug from the outside (without considering/knowing how it's implemented under the hood)
Yes that's what I meant. It's not a bug, just a consequence of using sets as specs
Sets with falsey values won't work
So don't do that
You've used a function that returns false for what you consider to be a valid value
actually about my s/select-keys
proposal earlier, why not making s/keys spec an c.l.Associative instance and allow us to compose this stuff with normal core fn?
the problem with that is that we capture the key set for describe and form, so you would lose that ability
that’s the reason it’s a macro and not a function that takes data now
@jrheard no, haven’t seen that
hi. I'm just starting to play with clojure.spec. I'm ok with some basic specs and validation that I've tried. But having trouble with even a trivial example of specing a higher-order fn. Here's what I'm seeing:
(def my-spec (s/fspec :ret string?))
=> #'user/my-spec
(s/conform my-spec (fn [j] (str j)))
IllegalArgumentException No implementation of method: :specize* of protocol: #'clojure.spec/Specize found for class: nil clojure.core/-cache-protocol-fn (core_deftype.clj:568)
(s/fdef my-spec :ret string?)
will be better for you right now
sorry, I misread that first def as s/def, let me read again
so the issue here is that to verify that the function you’ve passed is valid, it will generate args based on the :args spec for the function and invoke it
but you’ve passed no args spec
true. I was going with a simple example -- just validating that it is a fn that returns a string. I did try with args before, and was getting different errors, so I was trying to simplify the example.
although I am seeing some weird stuff on this path
(s/def my-spec (s/fspec :args (s/tuple integer?) :ret string?)) => user/my-spec (s/conform my-spec (fn [j] (str j))) IllegalArgumentException No implementation of method: :specize* of protocol: #'clojure.spec/Specize found for class: nil clojure.core/-cache-protocol-fn (core_deftype.clj:568)
ah, so conform takes a qualified keyword or symbol
(s/conform `my-spec (fn [j] (str j)))
will fully-qualify my-spec
@seancorfield yeah, very sure- compare clojure’s https://github.com/clojure/clojure/blob/master/src/clj/clojure/spec/test.clj#L19 vs cljs’s https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/spec/test.cljs#L19 [and also i’ve verified interactively]
@jrheard the idea in both of those is to pass a map of options through to test.check - I think the underlying option keys differ in clj vs cljs test.check
so there may be a disconnect between clj vs cljs and docs here
yeah, i think the cljs docs need to be updated to reflect the disconnect - https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/spec/test.cljc#L211
i was using :clojure.spec.test.check/opts as the docs recommend, but nothing was happening
I suspect so - prob should file a jira (and if you like a patch!)
ahh! it does say that in the spec guide! Maybe it would be helpful for the conform and valid? docstring(s) could also highlight that requirement.
sounds good, it’ll be my first of both of those 🙂 time to go read the contributing guidelines again
@mlimotte seems like there should be a better error message in this case
I will log that and add a patc hfor it
yep. that would help. here's another question. What's a recommended way to spec the requirement that the fn should be a one-arg fn? I tried:
(s/def my-spec (s/fspec :args (s/tuple identity) :ret string?))
(s/def my-spec (s/fspec :args #(= (count %) 1) :ret string?))
which results in
ExceptionInfo Unable to construct gen at: [0] for: identity clojure.core/ex-info (core.clj:4617)
ExceptionInfo Unable to construct gen at: [] for: clojure.spec$spec_impl$reify__1451@4e6417a0 clojure.core/ex-info (core.clj:4617)
(s/def my-spec (s/fspec :args (s/cat :j any?) :ret string?)
(i’ve made a jira account, username jrheard - alex, are you the right person to bug to add whatever necessary permissions to that account, or should i go bother someone else?)
you can add tickets with the starting permissions
it’s in core
it’s just (constantly true)
actually it does
I was more sketching than saying the actual def :)
(defn any?
"Returns true given any argument."
{:tag Boolean
:added "1.9"}
[x] true)
i see. I'm using clojure-future-spec w/ clojure 1.8, so this works:
(s/def my-spec (s/fspec :args (s/cat :j clojure.future/any?) :ret string?))
as does:
(s/def my-spec (s/fspec :args (s/tuple clojure.future/any?) :ret string?))
Was your choice of s/cat over s/tuple just stylistic, or is there something more significant?
@jrheard I added edit permissions for you as well in jira
@mlimotte tuple is fine too. we usually use regex ops to describe args. Args are effectively syntax and that’s what regex ops in spec are the best match for.
one benefit of cat is that you will get named parts for conforming the parts whereas you will get just indexes for tuple
so that affects both the conform result (which you’ll see in the :fn spec) and the explain errors
generally I find being able to tie the spec to the arg name helpful
@alexmiller - i just added a couple patches to the ticket. is there anything else i need to do, or should i just wait and expect to get a response at some point in the future? i’m asking you this not because i’m in an incredible hurry to have someone immediately look at this jira issue this very second; i just want to make sure i’m not missing some “notify the maintainers” step before i close all these tabs 🙂 thanks!
For cljs-1808
Could there be a problem with s/tuple? I have the code below, which bombs w/ the error below, but if I change the '(s/tuple integer?)` to (s/cat :a integer?)
than it works.
(defn myfn [a] (str a))
(s/fdef myfn :args (s/tuple integer?) :ret string?)
(stest/instrument `myfn)
(myfn 1)
I get
=> #'user/myfn
=> user/myfn
=> [user/myfn]
ExceptionInfo Call to #'user/myfn did not conform to spec:
val: (1) fails at: [:args] predicate: vector?
:clojure.spec/args (1)
:clojure.spec/failure :instrument
:clojure.spec.test/caller {:file "form-init1342783443404146057.clj", :line 4, :var-scope user/eval30918}
clojure.core/ex-info (core.clj:4617)
@mlimotte s/tuple
specifically expects its input to be a vector, the :args list is not a vector
i see. in retrospect, kind of obvious from the error message. i should have gotten that.
Can s/conform handle coercion? For example, could I have an s/def that defines a ::uuid to be either a (valid) string or a java.util.UUID, and always conforms it to a java.util.UUID?
I’d pass a conformer (the result of s/conformer) to s/def, e.g. (s/def ::foo (s/conformer #(do-stuff %)))?
Answered my own question; yep, that works
Thanks!
Now I have to decide if that’s a good pattern, or if it’d be better to keep input coercion completely separate from specs
conforming is not something that you want in your hot loop though IIUC; it’s a measurable performance impac
but at the edges? sure; you’re gonna do that anyway, might as well have it be declarative
Hmm, that’s a good point
If it’s only at the edges, then I can assume the input will be a string (for a web service, at least)
Though if the backend requires a UUID, I’d probably still want to coerce it as early as possible.
I guess if the coercion is lossless, it’s safe; but if it’s lossy, I’d want to have an explicit coercion step.
Heading out
Thanks for the help!