This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-02-08
Channels
- # aleph (11)
- # arachne (7)
- # aws (1)
- # bangalore-clj (4)
- # beginners (24)
- # boot (128)
- # bristol-clojurians (23)
- # cider (1)
- # cljs-dev (43)
- # cljsrn (6)
- # clojure (178)
- # clojure-austin (3)
- # clojure-chicago (1)
- # clojure-dusseldorf (14)
- # clojure-finland (15)
- # clojure-france (6)
- # clojure-italy (18)
- # clojure-portugal (2)
- # clojure-russia (67)
- # clojure-spec (148)
- # clojure-uk (55)
- # clojurescript (199)
- # core-async (4)
- # cursive (18)
- # datascript (5)
- # datomic (120)
- # devcards (3)
- # dirac (53)
- # emacs (11)
- # events (3)
- # gsoc (7)
- # jobs (1)
- # lein-figwheel (25)
- # leiningen (5)
- # lumo (12)
- # off-topic (29)
- # om (174)
- # om-next (2)
- # onyx (7)
- # perun (10)
- # protorepl (6)
- # re-frame (12)
- # remote-jobs (1)
- # ring (19)
- # ring-swagger (25)
- # rum (6)
- # spacemacs (13)
- # sql (3)
- # untangled (88)
- # yada (7)
or somehow tell test.check.generator/map
or hash-map
that I want keys to be out of the spec?
cljs.user=> (require '[clojure.spec :as s])
nil
cljs.user=> (s/form (s/keys :req-un [::foo] :opt-un [::bar]))
(cljs.spec/keys :req-un [:cljs.user/foo] :opt-un [:cljs.user/bar])
ofc if it's a more complicated spec you'll need to walk the form finding the key specs
cljs.user=> (s/form (s/and (s/keys :req-un [::foo] :opt-un [::bar]) identity))
(cljs.spec/and (s/keys :req-un [:cljs.user/foo] :opt-un [:cljs.user/bar]) cljs.core/identity)
what is the standard way of memoizing spec checks? I have a recursive data structure (say a tree), and I don't want it to have to verify the property holds on subtrees every time -- I want to somehow 'cache' "this node passed spec xyz" somehow
when memoizing, can we somehow do it with "weak references" so that instead of having gc leaks, the object is thrown away when it's no longer needed ?
I suspect one nice thing about meta data over memoization is that with meta data, with the object is no longer needed, and the gc gcs it, it takes away the metadata too
with memoization, I fear it'll prevent the gc from doing its work because the spec function will keep th eobject around
why are you running spec checks over and over on a large structure? like, just don't do that
if I have input argument spec validation on, wouldn't it run the spec every time the function is called?
now, if this structure is recursive (say: this is a tree, where the sum of every subtree is a multiple of 3), then it ends up checking the entire tree every time
my first concern with that question is it sounds like you are planning to turn on spec instrumentation outside of your test suite
that is true; is it bad to have spec instrumentation turned on in production code? if it's a costant time hit, I would not mind
the intended use, as far as I understand, is to turn instrumentation on when running your test suite
I think assertions are good in dev code (not just in test quite). I'm trying to use spec as "ultra powerful assert" in dev code. You're saying this is bad, and I should not use spec as an assert ?
For some functions, I want to assert that their input satisfies certain pre-conditions. These pre-conditions can be expensive (checking all subtrees); therefore, I'd like a way to cache this. Now, given that I want to do the above, can I do this with spec, or would spec be a bad match? I'm hoping that spec can do this, as I really like spec's language.
imo many many people will use spec the way you describe, but there are some things to be wary of: it will be slow. if you spec any functions that you pass as arguments those functions will be called multiple times to check that they pass the spec as part of instrumentation. instrumentation does not check return values
@bfabry: we can assume I don't need to "run specs on functions passed as inputs" -- and my only conern at the moment being simple data structures and recurisve data sturcutres // functions are way too hard
if you don't have any automatic checking turned on, checking will only happen when you ask for it, so just don't ask for expensive checks more than once
fwiw there's no reason you can't use spec's data description language to validate data without using instrumentation. just call s/valid or s/conform directly. you could even wrap your calls to s/valid and s/conform in something that memoizes based on the object's reference id or whatever if you really want to tweak performance of that validation
you could even still associate the spec with the function, so you get the documentation, generation stubbing when wanted etc, just don't call s/instrument
http://blog.fogus.me/2009/12/21/clojures-pre-and-post/ <-- this existed 7 years ago, finally starting to use it now
the spec guide under "Using Spec for Validation" gives the :pre and :post example for reference
@joshjones: found it, thanks!
Is it expected behaviour to get a clojure.spec/unknown
explanation if a non-conforming spec has a custom generator, like this:
(s/def ::a (s/with-gen string? identity))
(s/def ::some-map (s/keys :req [::a]))
(s/explain ::some-map {::a nil})
In: [:app.deals/a] val: nil fails spec: :app.deals/a at: [:app.deals/a] predicate: :clojure.spec/unknown
Insead of
(s/def ::a string?)
...
In: [:app.deals/a] val: nil fails spec: :app.deals/a at: [:app.deals/a] predicate: string?
What sort of generator is identity
?
(hmm, and doesn't with-gen
take a nilary function that returns a generator?)
Yup, "Takes a spec and a no-arg, generator-returning fn and returns a version of that spec that uses that generator" -- identity
does not satisfy that.
So (s/with-gen anything identity)
doesn't make sense... you're not going to get a valid generator from that?
Yeah, if you (s/exercise ::a)
you'll get
boot.user=> (s/exercise ::a)
clojure.lang.ArityException: Wrong number of args (0) passed to: core/identity
which is what I'd expect.So the error from s/explain
isn't surprising but it is perhaps a bit misleading @onetom
i observed the very same behaviour in our actual app with this real generator:
(s/def :deal/name (-> string?
(s/with-gen #(gen/fmap
(fn [s] (str "<DEAL-NAME-" s ">"))
(gen/string-alphanumeric)))))
(s/def ::a (s/with-gen string? gen/string-alphanumeric))
(s/def ::some-map (s/keys :req [::a]))
(-> ::some-map s/gen gen/generate)
(s/explain ::some-map {::a nil})
=> #:boot.user{:a "lybK4CU4teXKCnErk9h5ajdGBu"}
In: [:boot.user/a] val: nil fails spec: :boot.user/a at: [:boot.user/a] predicate: :clojure.spec/unknown
OK, that does s/exercise
...
Yeah, that sounds like it's worth a JIRA issue...
I'd expect a better message, at least.
and this is the very first time i wrote a custom generator for an actual real-world use case... thats my generic experience with software... sometimes im wondering im just unlucky. what comforts me slightly is that i have a friend who is an order of magnitude "unluckier" 🙂
ok, i will make a JIRA issue. (this will be my first JIRA issue... im already worried what will happen ;)
Functionally-linked people are very useful in QA'ing software 🙂
I've created http://dev.clojure.org/jira/browse/CLJ-2107 but it seems I can't edit it to correct the markdown syntax in it 😕
How's that? (edited)
(uses {code}
around code not three backticks)
and {{
}}
instead of backticks for inline code.
with-gen is a function, so it's arguments are evaluated, so the string? argument to with-gen is a function object, when spec is trying to find the name to report it does some stuff, which for symbols and keywords reports a good name, but for other Objects (including function objects) you get :clojure.spec/unknown
user=> (s/def ::a (s/with-gen (s/spec string?) identity))
:user/a
(s/def ::some-map (s/keys :req [::a]))
:user/some-map
user=> (s/explain ::some-map {::a nil})
In: [:user/a] val: nil fails spec: :user/a at: [:user/a] predicate: string?
nil
user=>
wrapping with s/spec allows the spec macro to capture the meaningful, the symbol before evaluation
s/spec also takes :gen so you can avoid calling with-gen separately, I find it nicer personally
I have some data which I can neatly define a spec for using the regexp combinators of spec, which is great because I can generate example data for free, but for the functions I'm testing it's quite important that the returned sequences are vectors rather than list
coll-of
and every
allow you to define the type of the sequence, but they don't know about the structure of the sequence
is there any easier way to force the generators to produce vectors than using a custom generator that calls vec
on them?
Hey guys, I'm trying to use the simple-type
generator in one of my specs, but I can't seem to get it to work. I currently have (s/def ::any (s/spec (fn[] (true)) :gen #(gen/simple-type)))
but it seems that whatever permutation of the :gen
value I use it returns an error
@linuss Can you paste the error you get? At any rate, the parens around (true)
look wrong, that means you are calling true
as a function
@dergutemoritz Ah, thanks! Yeah, that doesn't help. However, I'm still getting the following error: java.lang.IllegalArgumentException: No value supplied for key: (fn* [] (gen/simple-type))
java.util.concurrent.ExecutionException: clojure.lang.ArityException: Wrong number of args (1) passed to: specs/fn--10638
That's the error I keep getting
@linuss Ah, right, your spec predicate function needs to accept a single argument.
So (fn [x] true)
would do the trick. You can use any?
instead, too, which is the core function with the same behavior.
Plus that clojure.spec
has a default generator for it
You're welcome!
@linuss It's in clojure.core
since 1.9
...
{:result #error {
:cause "Could not locate clojure/test/check/generators__init.class or clojure/test/check/generators.clj on classpath."
:via
[{:type java.io.FileNotFoundException
:message "Could not locate clojure/test/check/generators__init.class or clojure/test/check/generators.clj on classpath."
:at [clojure.lang.RT load "RT.java" 458]}]
:trace ...
Yes. [org.clojure/test.check "0.9.0"]
@pbaille Depends on what you mean by dispatching sytem 😄
Not really.. you can certainly use s/conform
in a multimethod's dispatch function, though
Which seems like it could be a useful thing
i've done a little gist about this, doesn't look really nice... https://gist.github.com/pbaille/b1bc0d05c2ec428e220fa28d28c8354f
I am trying to write a definition for “weeks in a year” and having difficulty checking that the integer is < 53.
(s/def ::valid-weeks-in-year (s/and ::non-negative-integer #(< % 53)))
It is failing with
java.lang.ClassCastException: clojure.lang.MapEntry cannot be cast to java.lang.Number
. What am I missing?(s/def ::non-negative-integer (s/and int? #(>= % 0)))
(s/def ::valid-weeks-in-year (s/and ::non-negative-integer #(< % 53)))
(s/valid? ::valid-weeks-in-year 10) => true
@cryptorat int-in
?
https://clojure.github.io/clojure/branch-master/clojure.spec-api.html#clojure.spec/int-in
The problem seems to lay in using
`(s/def ::non-negative (s/or :positive pos?
:zero zero?))`
instead of
#(>= % 0)
@cryptorat a non-negative integer is also known as a natural integer. 1.9 has a predicate for this, so you can just use nat-int?
as your predicate, although for your case, as @ghadi said, int-in
is probably more appropriate since you need an upper bound
My understanding is that whether or not zero is included in the set of natural numbers can be debated. I figured best to avoid that in case someone changes their mind. Too cautious perhaps.
this is a good point — i doubt the definition will change in the clojure universe but good catch nonetheless. but as you have already seen, (s/int-in 0 53)
is much better anyway for your case
If you have two maps where map :a
has :a/id
and map :b
likes to reference a particular map :a
. How would you call the key in :b
? Would you just use :a/id
or would you create a new key called something like :b/a-ref
?
either {:a/id 1 :b/name “foo”}
references {:a/id 1 :a/name “bar”}
or {:b/a-ref 1 :b/name “foo”}
references {:a/id 1 :a/name “bar”}
a big disadvantage of using :a/id
also for such kind of references is, that not every map containing an :a/id
can be considered to be an :a
.
To make it more spec relevant - I ask because in spec keywords are used as names for something like types and that keywords are also used in maps to name something like attributes.
@akiel I'd go with a different name, i.e. the :b/a-ref
version. Because a reference to a thing is not the same as the thing itself after all.
Then again, embedding the data directly shouldn't really have that much of an impact if you have the thing that is pointed to in memory anyhow
@dergutemoritz I lean also towards using :b/a-ref
. Regarding embedding directly you are right, it won’t cost memory. There I was wrong.
@dergutemoritz But it would cost on wire. I need to transport that data over wire.
Another reason to name it :b/something
is that you can then use a name that describes the relationship. E.g. if :a
is :person
and :b
is :book
then you could have (s/def :book/author :person/id)
which I'd say would even justify leaving off the -ref
suffix.
@dergutemoritz Yes you are right - role names.
@dergutemoritz A related thing: Would you go for all keys in a person to start with the namespace :person
or would you also use other common keys in a person? Like :person/name
, :book/name
vs. just :common/name
.
@akiel I don't know, I think that's subject to an ontological debate you have to have with your domain experts 🙂
Note that you could have both in a way: (s/def :common/name string?) (s/def :person/name :common/name)
That way you could attach additional meaning to :common/name
and also have it influence :person/name
@dergutemoritz The domain is given in my case. But I just think about map keys in relation to specs. With 20 different names, you end up with 20 specs for names which are all the same. I’m not sure if thats a bit of an antipattern now regarding to spec.
It really depends on your domain
@dergutemoritz Than you have specs like :common/name
which are never used as keys in a map. That doesn’t have to be a problem - just thinking about it.
Yeah, that would be an abstract spec probably
I guess the question is whether the concept of a name is universal in your domain or specific to each entity. Or maybe a mixture of those.
But I don't feel like I've fully figured this out, yet, either
For example I have a transaction type which can have values like :insert
or :update
. It has the same meaning and the same values for each entity. Should I have a transaction type for every entity or just one?
If it has the same meaning in all contexts, then I'd go with a single one
@dergutemoritz Thanks, that sounds reasonable. Same name for the same thing. 🙂
:thumbsup:
At least that's my current understanding of things 🙂 If anyone else has another interpretation, I'm all ears
Was that meant for a different channel @zane ?