This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-06-22
Channels
- # bangalore-clj (6)
- # beginners (110)
- # boot (49)
- # cider (13)
- # cljs-dev (35)
- # cljsrn (5)
- # clojure (145)
- # clojure-conj (3)
- # clojure-dev (60)
- # clojure-italy (2)
- # clojure-nl (3)
- # clojure-russia (3)
- # clojure-serbia (1)
- # clojure-spec (116)
- # clojure-uk (58)
- # clojurescript (235)
- # cursive (14)
- # datascript (7)
- # datomic (31)
- # dirac (144)
- # emacs (1)
- # events (1)
- # hoplon (12)
- # leiningen (11)
- # luminus (60)
- # lumo (19)
- # off-topic (18)
- # om (74)
- # onyx (5)
- # pedestal (13)
- # precept (3)
- # re-frame (3)
- # reagent (15)
- # remote-jobs (7)
- # ring-swagger (25)
- # rum (1)
- # untangled (53)
- # vim (3)
In Rich's speculation keynote he mentioned that there are ways to check if backwards compatibility is broken in both set theory and regex theory. The set one would presumably be something like:
(defn breaking-change? [old-required-keys new-required-keys]
(not (set/subset? new-required-keys old-required-keys)))
Are there any links to info about the regex part? Or am I misunderstanding something?madstap: the regex part is based on Matt Might’s papers with regex derivatives and I believe Rich has spent enough time with it to believe that the math there makes this computable. I’m having a strong sense of deja vu - have you asked this before?
Must have been someone else, or my addled brain
Hey is there function to coerce all keys in a map to namespace-qualified keys, other than the #::{}
syntax? I’m receiving an unqualified map from an API call, and would like to spec the result.
@sooheon if you just need to spec un-namespaced keys you can just do (s/keys :req-un [::my-key1 ::my-key2])
to require those keys un-namespaced, ie {:my-key1 "x" :my-key2 "y"}
But now when I pass around this map and assoc my own keys to it, it will have a mix of qualified and un- keywords…
s/keys can take vectors for both :req-un and :req if you have both namespace qualified and unqualified keys
@sooheon You could just use reduce-kv
and produce a new map from the old one with namespace-qualified versions of the keys (assuming you want the same qualifier on all of them).
Something like (reduce-kv (fn [m k v] (assoc m (keyword "qualifier" (name k)) v)) {} api-result)
ah cool! hadn’t thought of that. Was just hoping something like (into #::{})
would magically work
is this something that is recommended, btw? to use qualified keywords for everything?
I think it’s a reasonable approach for handling domain data within your system, yes.
Bear in mind tho’ that you probably want to keep those API result keys separate from your actual domain keys in order to avoid conflicts.
So you might have :external.api/name
and :my.domain/name
with different specs.
This is one of the reasons that the latest java.jdbc
lets you specify :qualifier
in the options on almost every call so you can get namespace-qualified keys in your SQL result hash maps.
I see. I think it’ll just take some experience (just like knowing how to split up and organize ns’es in the first place)
Bear in mind that the namespace-qualifiers on keywords do not need to correspond to actual code namespaces so you have a lot of flexibility.
For example, we use a qualifier of wsbilling
for all entities that relate to our (World Singles) billing system but we don’t have a namespace called wsbilling
.
When we accept JSON/Clojure data from outside (APIs, databases), we can normalize it to whatever qualifier we want to distinguish it, within our system, from our billing data.
(if this was a public Clojure API we’d use a reverse-domain-name prefix, like com.worldsingles.billing
, I expect)
You can choose how to keep unique groups of entity names separate from each other.
@sooheon @seancorfield I made a function a while back to namespace-qualify a map. here's what i have, if it's useful to anyone
(defn map->nsmap
"Creates a namespaced-map from a standard one and a namespace (obj or string)"
[m n]
(reduce-kv (fn [acc k v]
(let [new-kw (if (and (keyword? k)
(not (qualified-keyword? k)))
(keyword (str n) (name k))
k) ]
(assoc acc new-kw v)))
{} m))
@seancorfield I’ve still got to experience first hand how ns qualifying keys works with stuff like destructuring, and making sense of how I want to organize things, but thanks for the pointers
That’s good for preserving existing name-qualification. Sometimes you need to override that. And of course my simplistic example only works for keys that you can call name
on (and assumes you want all-keyword keys back out)!
@sooheon Yeah, it takes some getting used to, after so many years of unqualified keywords!!
In terms of destructuring:
user=> (let [{:foo/keys [a b c]} {:a 1 :foo/b 2 :c 3 :foo/c 4}] (println a b c))
nil 2 4
yes, i wrote it to behave the way the #:
reader macro behaves, namely, it does not disturb non-qualified keys
also
user=> (let [{:keys [a b foo/c]} {:a 1 :foo/b 2 :c 3 :foo/c 4}] (println a b c))
1 nil 4
So you can put qualifiers in the key vector — and you get unqualified symbols back out!Like this:
user=> (let [{::keys [a b c]} {:a 1 :user/b 2 :c 3 :user/c 4}] (println a b c))
nil 2 4
Note that we’re in the user
namespace so ::keys
means :user/keys
You can also do this (which I’m surprised is allowed):
user=> (let [{:keys [a ::b ::c]} {:a 1 :user/b 2 :c 3 :user/c 4}] (println a b c))
1 2 4
actually it seems to work by putting the ::
in the vector of keys too, though i'm not sure it's a good idea
(let [{:keys [a b c]} {:a 1 :foo/b 2 :c 3 ::c 4}] (println a b c))
1 nil 3
=> nil
(let [{:keys [a b ::c]} {:a 1 :foo/b 2 :c 3 ::c 4}] (println a b c))
1 nil 4
I expected that to be an error but apparently you can use keywords in the key vector? @alexmiller Is that supposed to be valid?
You can’t have multiple :keys
::keys
and :foo/keys
deconstructions separately at once, right? You’d do {:keys [a ::b foo/c]}
That’s my understanding, yes.
@seancorfield yes, we support keywords there so that you can use autoresolved keywords
Is that documented? (I guess the docs could have been updated since I last looked at them… 🙂 )
And you can have multiple keys destructurings at the same time for different namespaces
It was in the changelog when it went in but that was a while ago
Yup, the destructuring guide contains examples.
Not of ::k directly but of ::p/name
Rich and I worked on some updated docs for destructuring but I can't remember if those actually got all the way done
It’s fairly comprehensive, TBH. Thank you. And Rich.
@alexmiller From my issue in the tweet, here's the tail end of a very long stack trace:
clojure.spec.alpha/regex-spec-impl/reify/gen* alpha.clj: 1672
clojure.spec.alpha/re-gen alpha.clj: 1601
clojure.core/every? core.clj: 2572
clojure.core/next core.clj: 64
...
clojure.core/map/fn core.clj: 2657
clojure.spec.alpha/re-gen/ggens/gen alpha.clj: 1582
clojure.spec.alpha/re-gen alpha.clj: 1597
clojure.spec.alpha/gensub alpha.clj: 275
clojure.core/ex-info core.clj: 4617
clojure.lang.ExceptionInfo: Unable to construct gen at: [:exception] for: (instance? java.lang.Throwable %)
clojure.spec.alpha/failure: :no-gen
clojure.spec.alpha/form: (clojure.core/fn [%] (clojure.core/instance? java.lang.Throwable %))
clojure.spec.alpha/path: [:exception]
What I don't get is why it is trying to create a generator. It just supposed to be validating inputs into our schema/compile function.
Basically, the function I'm passing in doesn't have a fspec
, so I'm going to add one and see if that helps.
Right, so that was my guess
the way that fspec args are validated in instrumentation is by using the fspec’s generator to generate values and invoke the function
and check the ret and fn spec to verify the function with random inputs is valid according to the fspec
thus all fspec’s should have an args spec that generates
the surprising bit here is that they need that not just for check, but also for instrument
same is also true of fdef for check, but not for instrument
(because checking an fdef involves generating using its args generator)
Just wondering, why not just wrapping args/ret of fspec fns with s/valid? when instrumented
not sure if Rich considered that or not, haven’t talked to him about it
alexmiller: just wondering: wouldn't it be better to keep https://dev.clojure.org/jira/browse/CLJ-1936 open then?
So I'm a bit lost .... is there no way I can simply declare what my callback should look like without coming up with a generator for the callback?
hlship: in short no, if you want to also validate the fdef. other options are to override the fdef args generator or to use a generator override on instrument
actually, ifn?
is better if you go that route
(gen/sample (s/gen (s/spec ::clojure.core.specs.alpha/map-bindings)))
ExceptionInfo Couldn't satisfy such-that predicate after 100 tries. clojure.core/ex-info (core.clj:4725)
@gfredericks in core.specs.alpha
the clojure.core specs that got split out into a separate lib when spec got split out
@alexmiller I’m confused by the definition of ::core.specs/map-bindings
. What does it mean for (s/every ... :into {})
I don’t see a guarantee that the every coll has an even number of elements, nor a requirement that the input coll is a map
if you're wondering where the such-that
error is coming from, I suspect https://github.com/clojure/core.specs.alpha/blob/c33689f75dbe3188ee00b32bb798bcd0cfd6cacc/src/main/clojure/clojure/core/specs/alpha.clj#L36
@arohner every branch of map-bindings seems to be a pair, does that explain your confusion?
it seems very weird to me that :into is used for generating, but the resulting conform doesn’t change
the real source of my trouble is just below, when I have to handle (s/def ::map-binding-form (s/merge ::map-bindings ::map-special-binding))
sorry, just reading this now. where is your question at now?
note that s/merge doesn’t flow conformed values so only the last map spec in the merge will matter for the conformed value
::map-binding-forms
takes two maps, because of s/merge
, but map-bindings
is not guaranteed to return maps, because it uses :into
and not :kind
I don’t agree with the last clause there?
both :into
and :kind
affect s/every gen
I’d say that’s not a supported map binding form
I guess we could use :kind to narrow that
right, spectrum is currently picky about that, and I’m trying to understand it / make it work
spectrum says map-binding isn’t necessarily a map, so the merge isn’t guaranteed to work in all cases
yeah, that seems good. if you want to file a jira, we can try to get that in next release
if you want to make a patch, that will be faster than me making it (as I can screen it)
patch against core.specs.alpha of course
What's the best way to define a function :args
spec for a function which takes a single argument?
@peeja - same way as you do for any :args
spec
(s/fdef your-func
:args (s/cat :single-arg some-pred?)
...)
agreed and to take that a step further, it’s useful to use a regex op (s/cat) here because the conformed value will be a map with the arg name as the key, and that’s useful in the :fn spec or in explanations