This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-06-07
Channels
- # beginners (142)
- # boot (18)
- # cider (39)
- # cljs-dev (2)
- # cljsrn (11)
- # clojars (8)
- # clojure (51)
- # clojure-brasil (3)
- # clojure-italy (22)
- # clojure-losangeles (2)
- # clojure-nl (20)
- # clojure-nlp (2)
- # clojure-russia (4)
- # clojure-serbia (2)
- # clojure-spec (90)
- # clojure-uk (147)
- # clojurescript (116)
- # data-science (10)
- # datomic (189)
- # devcards (31)
- # duct (6)
- # emacs (12)
- # fulcro (16)
- # graphql (36)
- # juxt (1)
- # off-topic (5)
- # om (11)
- # overtone (2)
- # pedestal (4)
- # perun (1)
- # portkey (17)
- # reagent (6)
- # reitit (3)
- # shadow-cljs (57)
- # spacemacs (45)
- # specter (8)
- # tools-deps (46)
Generally anything like this should be considered a bug (if it’s reproducible). One thing that could possibly lead you down this path at the repl is changing a spec but not redefining a spec that used it. Specs are compiled at definition time so you can get a mixture of things that way.
Oh. I made a poor man's version of spec using defs and thought you got around that with the central database of specs and always getting a "fresh" copy. I'll make sure to have up to date definitions. Thanks
What is the most idiomatic way to apply s/cat
, knowing that it is a macro?
A future way to do this will be to write a spec for s/cat, write conform data, and s/unform back to a spec.
See CLJ-2112 for some early work on that
to apply a macro, you need another macro (and to do it at runtime to something that is not a literal in your source file or created when loading the file you need to be compiling new code, eg. eval)
I will use a macro then. Do you know why it was made as a macro? (most importantly, why only as a macro)
That would have been good to have the macro and the functions and let the users choose between them.
Completely agree. The prevalence of macros in spec is my number one complaint with it.
s/cat
and sm/cat
“macro contagion” … it reminds me of the “const hell” in c++
@vincent.cantin spec was designed for humans to write, so macros are fine. There is work in the pipeline to open up programmatic access to spec, which will mean functions.
Macros are not fine. They prevent higher-order code and partial application (to name a couple of issues). And of course they result in 'macro proliferation' as well.
So, for now, you might be going through a lot of (macro) pain for nothing, depending on how quickly you need the thing you're trying to build...
The task I gave myself is probably making me a complainer by design: I am trying to do the most I can do with Spec.
You are right, it is not related to the normal use case.
It's interesting... over time, the Clojure/core folks have produced a number of things that in the rush of new excitement around a feature, lots of people almost immediately try to do stuff with each new feature that's outside the design goals 🙂
@U04V70XH6 I was doing that: https://github.com/green-coder/TIS-100-Spec My problem with the cat being a macro was for the implementation of my token spec.
I wanted to see if it was possible to do it that way. Well … it is possible, but not convenient.
And how is performance? 👀
eh eh .. I don’t know, it does not matter here.
I wished that cat
could accept nil
instead of a token for specs which we do not need to know about when we use conform
.
in regex terms, it would mean a “non-capturing” expression.
I'm wondering how many times folks have to say "spec is not a parser"... ? :rolling_on_the_floor_laughing:
😄 that means that the community would like to use it in that way too.
If you get the grammar right and each spec generates then you have the possibility of generating random but syntactically valid TIS-100 programs... that you could then run and see what they do! 🙂
You start to see some of my goals
However spec is definitely sold as a parser, maybe unintentionally. "Check your inputs and, oh here is the parse tree".
I think it says a lot about how the features have been such great building blocks over the years, as well as how laser-focused many of the features have been.
I don't know if you've done much with clj
and deps.edn
yet? That seems to be attracting the same "let's see what crazy things we can do with this" experimentation.
I don't consider wanting to apply or partially apply a macro to be a "crazy experimentation". It's very much just a thing that advanced [meta-]programmers want to do to keep their code concise.
s/every or s/coll-of will interpret :kind
as a predicate only (not spec) during s/valid?
s/conform
etc
I think it is because the macros generate a cpred unconditionally interpreting kind as a pred
(s/def ::vector vector?)
=> :user/vector
(s/valid? (s/coll-of any? :kind ::vector) [])
=> false
(s/explain (s/coll-of any? :kind ::vector) [])
Success!
=> nil
(s/explain (s/coll-of any? :kind (s/spec vector?)) [])
Success!
=> nil
(s/valid? (s/coll-of any? :kind (s/spec vector?)) [])
ClassCastException clojure.spec.alpha$spec_impl$reify__1987 cannot be cast to clojure.lang.IFn user/eval3301/fn--3303 (form-init7886713966146839296.clj:1)
in checking against the spec, it will call (::vector [])
which returns nil and fails the spec. In explaining, it is aware that [] satisfies the ::vector
spec
(macroexpand '(s/coll-of any? :kind (s/spec vector?)))
=>
(clojure.spec.alpha/every-impl
(quote any?)
any?
{:clojure.spec.alpha/describe (quote
(clojure.spec.alpha/coll-of
clojure.core/any?
:kind
(clojure.spec.alpha/spec clojure.core/vector?))),
:clojure.spec.alpha/conform-all true,
:clojure.spec.alpha/cpred (fn*
[G__3317]
(clojure.core/and ((s/spec vector?) G__3317))),
:kind (s/spec vector?),
:clojure.spec.alpha/kind-form (quote
(clojure.spec.alpha/spec clojure.core/vector?))}
nil)
> :kind - a pred/spec that the collection type must satisfy, e.g. vector? (default nil) Note that if :kind is specified and :into is not, this pred must generate in order for every to generate.
I'm trying to write a spec for a function that takes in another function. I have the spec for the function passed in, but am getting "Couldn't satisfy such-that predicate after 100 tries."
. Is that because I need to write a generator for functions that are arguments?
such-that acts as a filter, it's saying it wasn't finding anything that the filter accepted
how is it spec’ed?
(sorry, was at lunch) Here's how I have it spec'd:
(s/def ::ruleset-id keyword?)
(s/def ::rule-name keyword?)
(s/fdef ::fetching-fn
:args ::ruleset-id
:ret (s/coll-of ::rule-name))
(s/fdef rules-for-rulesets
:args (s/cat :fetching-fn ::fetching-fn
:ruleset-ids (s/coll-of ::ruleset-id))
:ret (s/map-of (s/and set? (s/coll-of ::ruleset-id))
(s/and set? (s/coll-of ::rule-name)))
;; Ensure each rule only appears in the values only once so we
;; don't run them multiple times
:fn #(->> % :ret vals (reduce concat) frequencies vals (every? (partial = 1))))
could you try changing that (s/and set? (s/coll-of ::the-spec))
to (s/coll-of ::the-spec :kind set?)
@bmaddy
(because s/and
makes a generator from whatever the first predicate is, and set?
is unlikely to produce values that will satisfy ::ruleset-id
)
oh, another potential issue: ::fetching-fn
's :args
spec should probably be (s/cat :whatever ::ruleset-id)
instead of just ::ruleset-id
?
yeah I just realized the s/and
issue probably has nothing to do with the such-that
issue
((gen/generate (s/gen ::fetching-fn)) :foo)
[:K5.SY.if_!_._F6U/_
:p1Je.*-/*
:LbrF_/ZP]
That's so cool. 😄yeah FYI the fn generators (I think) just generate functions that'll return values generated from their :ret
spec
I'm having a bit of trouble getting just the right bit of output for one of my specs. This is for expound 0.7.0. Context: https://github.com/walmartlabs/lacinia/blob/a09684af4f2cabf23eb3d315bba0adad66787b57/src/com/walmartlabs/lacinia/schema.clj#L275 https://github.com/walmartlabs/lacinia/blob/a09684af4f2cabf23eb3d315bba0adad66787b57/src/com/walmartlabs/lacinia/expound.clj
I've made slight changes:
(s/def ::type (s/or :base-type ::type-name
:wrapped-type ::wrapped-type))
(s/def ::wrapped-type (s/cat :modifier ::wrapped-type-modifier
:type ::type))
(s/def ::wrapped-type-modifier #{'list 'non-null})
and
(defmsg ::schema/wrapped-type-modifier "a wrapped type: '(list type) or '(non-null type)")
But I get:
(binding [s/*explain-out* expound/printer]
(s/explain ::schema/field {:type '(something String)}))
-- Spec failed --------------------
{:type (something ...)}
^^^^^^^^^
should be one of: (quote list), (quote non-null)
-- Relevant specs -------
:com.walmartlabs.lacinia.schema/wrapped-type-modifier:
#{'non-null 'list}
:com.walmartlabs.lacinia.schema/wrapped-type:
(clojure.spec.alpha/cat
:modifier
:com.walmartlabs.lacinia.schema/wrapped-type-modifier
:type
:com.walmartlabs.lacinia.schema/type)
:com.walmartlabs.lacinia.schema/type:
(clojure.spec.alpha/or
:base-type
:com.walmartlabs.lacinia.schema/type-name
:wrapped-type
:com.walmartlabs.lacinia.schema/wrapped-type)
:com.walmartlabs.lacinia.schema/field:
(clojure.spec.alpha/keys
:req-un
[:com.walmartlabs.lacinia.schema/type]
:opt-un
[:com.walmartlabs.lacinia.schema/description
:com.walmartlabs.lacinia.schema/resolve
:com.walmartlabs.lacinia.schema/args
:com.walmartlabs.lacinia.schema/deprecated])
-------------------------
Detected 1 error
=> nil
@hlship Ah, yes, I’ve run into this as well. The problem is that defmsg
is narrowly applied to predicates, not any type of spec.
It’s a reflection of the original problem defmsg
solved: trying to provide more “human-friendly” messages instead of string?
int?
but that’s an arbitrary restriction, and I think it should be generalized to any spec, so you can add messages across the board
I haven’t tried it, but what happens if you replace #{'list 'non-null}
with (fn [x] (contains? #{'list 'non-null} x)
?
Even more generally, I think I should make sure to respect a registered message regardless of the spec type
frankly, I wasn’t sure anyone was really using Expound’s capability to register messages, so I wasn’t sure if it was really important. Glad to know you are using it 🙂
I’ve recently gone down a rabbit hole of trying to generate specs to test Expound but it’s taken up a lot more time than expected. I’m going to pause that effort and switch over to bug fixing for a bit
I'm very glad you implemented those messages, I think it can make a major difference. In general, I've gotten the rest of the team addicted to Expound ... we use a fair amount of clojure.spec, but previously, spec errors were treated as a boolean — they were so hard to parse, we just looked at code changes to figure out how to make them go away. Now we have a proper guide to exactly what's wrong.
@hlship I’d be interested in your feedback on another potential feature: message-fns https://github.com/bhb/expound/pull/96
Well, as a library developer, I want expound to be an optional dependency, so I don't think I could use this.
Oh sorry, I was unclear in my description on this issue: it works like messages today in the sense that there is no effect if someone isn’t using expound.
The only difference is that instead of registering a static string, you can register a function which will be called to generate the string
This would let you, say, augment the existing expound error message, or use the problem to craft a custom message. But perhaps in your use case, the static strings are sufficient.