This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-10-30
Channels
- # announcements (15)
- # beginners (143)
- # boot (2)
- # calva (48)
- # cider (93)
- # cljsrn (2)
- # clojure (127)
- # clojure-europe (3)
- # clojure-italy (8)
- # clojure-losangeles (8)
- # clojure-nl (10)
- # clojure-spec (67)
- # clojure-uk (51)
- # clojurescript (20)
- # cursive (9)
- # data-science (2)
- # datomic (10)
- # duct (13)
- # figwheel-main (1)
- # fulcro (74)
- # instaparse (10)
- # jobs (3)
- # joker (8)
- # juxt (4)
- # lumo (1)
- # malli (11)
- # nrepl (3)
- # off-topic (4)
- # pathom (5)
- # pedestal (6)
- # planck (5)
- # re-frame (18)
- # reagent (5)
- # reitit (17)
- # shadow-cljs (165)
- # sql (30)
- # vim (12)
- # xtdb (6)
Is there a canonical way to generate only a subset of the “types” from a multi-spec?
You can force a multimethod clause to be selected with the following:
(s/gen (<multispec> <dispatch-arg>)))
To pick a subset of the multispecs you can make a collection of the valid dispatch args, then randomly sample that.
Hmm... doing that is yielding maps that have the correct shape for the type, but the value of the dispatching type key is random, not the dispatch value.
I'm guessing it's because the result of (s/gen ...)
isn't flowing through the multi-spec's retag.
That all depends on how tight your multispec clauses are. You can override what you get with fmap
like this:
(clojure.test.check.generators/fmap #(assoc % ...) (s/gen (<multispec> <dispatch-arg>)))
For example, I have a multispec where I dispatch on two keys in the map I generate. The keys are unqualified, meaning I can strictly enforce that for a particular clause, I always get a particular value. Like this:
(s/def ::type #{:foo})
(defmethod multispec-fn :foo
[_] (s/keys :req-un [::type ...]))
So even if the s/keys
is part of a multispec for a dispatch value that involves :foo
, because the dispatch is done based on the value of a function applied to the data this way I make sure that when the above clause is selected, :foo
will be the value of the key :type
.
Using the code here: https://clojure.org/guides/spec#_multi_spec
user=> (map :event/type (gen/sample (s/gen :event/event)))
(:event/error :event/search :event/search :event/search :event/error :event/search :event/error :event/search :event/search :event/error)
user=> (map :event/type (gen/sample (s/gen (event-type {:event/type :event/search}))))
(:+/- :*/. :*/gq :IZ/K :./.35 :YT/* :d/B :*Pk/q2L6 :Lf/J! :+/M-)
@alexmiller Continuing the multi-spec example from the official guide, does this seem reasonable for the simple case of a kw retag?
That's why I think just calling the multi-method isn't enough. Generating via the multi-spec would flow through retag, but the code above doesn't.
I think going back to your original question, the answer is no, there is no way to (easily) generate a subset of the multispec values right now
there are a variety of hacks :)
you can choose a particular one and retag, or you can dive into the guts of the multimethod dispatch value map (which is what the multispec generator does)
Also :event/type
is defined as a keyword in the example, whereas in mine above I restrict that particular spec to a single value.
or you could s/and and only take events of a particular type
It just so happens that I have the same unqualified key in all the multispecs defined difrerently per multispec.
Don't be afraid to use it - I do in strategic places, typically around complex s/and
cases.
Well if it's an s/and
then simply provide your own generator by wrapping that in s/with-gen
.
Possibly. Though if you rely on custom conformers, s/and
is a very frequent thing to end up using.
retagging via fmap over the generated values from the multimethod is working though.
Found the such-that:
user=> (s/gen :event/event)
#clojure.test.check.generators.Generator{:gen #function[clojure.test.check.generators/such-that/fn--6656]}
I know it's a vague question, but what's the upgrade path from spec -> spec2 look like? I know there's changes coming in s/keys
and I guess the functions that validate/conform/explain might change, but I was wondering how much of the spec definitions will change?
I'm trying to figure out if we should start with spec2 directly, and deal with the breakage as it comes (I guess it's pre-alpha), or start with spec and deal with the upgrade breakage when it comes.
We've been trying to keep a branch of our codebase at work up to date on Spec 2 but it's required quite a few changes to some of our Spec 1 code and at this point, we've essentially "given up" on a straightforward migration. We plan to adopt Spec 2 for new code once it is out of pre-alpha status and run a mixed Spec 1 / Spec 2 codebase for a good long while.
I would use spec right now
the api functions - validate/conform/explain etc have gained some capabilities, but I expect will continue to work in spec 2 as is
any news on eta? we're also waiting on v2 release for some new features (looking in particular at new data spec format for "interpretation" of specs)
the spec op forms will mostly be the same, although keys -> schema/select will be a significant change and there are some differences around the use of sets, preds, functions, etc at thee top level
I don't have an eta but I'd guess it's on the order of months
@alexmiller Continuing the multi-spec example from the official guide, does this seem reasonable for the simple case of a kw retag?
We've been trying to keep a branch of our codebase at work up to date on Spec 2 but it's required quite a few changes to some of our Spec 1 code and at this point, we've essentially "given up" on a straightforward migration. We plan to adopt Spec 2 for new code once it is out of pre-alpha status and run a mixed Spec 1 / Spec 2 codebase for a good long while.
@orestis As Alex says, use Spec 1 for the time being. Conversion to Spec 2 may be easy or hard depending on how you use Spec 1.
The thing is I need to do closed map checking, recursively. I think that it can be hacked on top of spec1, but not sure if it’s already done and if not, if worth the effort to do myself.
Is the project a production-level one, or likely to be one within the next couple of months?
If not, I'd say give Spec 2 a go. Otherwise, stick to Spec 1.
I wouldn't, I'd say stick to spec 1 :)
there is no release of spec 2 available, because I do not think it is release-worthy yet. until then, I would not recommend for anything other than tire-kicking and feedback (which is very welcome)
Right, that's what I meant about not production-level -- if this is just an exercise project, go kick the tires. Otherwise, stick to Spec 1.
I really like where Spec 2 is going but it's a different beast in many ways from Spec 1...
The book Secure By Design from Manning writes about using Java classes with immutable properties and data contracts in the constructor, then only using this defined class (instead of using, say, a string for a bank account number) in order to prevent business logic bugs. The nice thing about this is the type system enforces a degree of defensive programming.
My question: is there an clojure.spec
analogue, or convention for this?
mostly I think that's trying to patch problems that are endemic to use of Java, rather than an actually good idea. however, I'd say the closest thing is to separate specs into two levels - domain definitions (::domain/bank-acct-no) and uses in specific contexts that alias those domain definitions
but this is really mostly just making complicated definitions once rather than repeating them
would you validate as a pre-condition every time you were to use ::domain/bank-acct-no? I suppose it's super cheap computationally
No, I would not
I would use instrument to check during dev/test