This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-10-21
Channels
- # aws-lambda (12)
- # beginners (20)
- # boot (1)
- # clara (15)
- # clojure (17)
- # clojure-dev (6)
- # clojure-greece (4)
- # clojure-russia (4)
- # clojure-spec (27)
- # clojure-uk (3)
- # clojurescript (22)
- # cursive (1)
- # data-science (3)
- # datomic (4)
- # fulcro (2)
- # juxt (2)
- # leiningen (2)
- # lumo (19)
- # protorepl (13)
- # re-frame (42)
- # reagent (5)
- # ring-swagger (1)
- # shadow-cljs (89)
- # spacemacs (1)
- # uncomplicate (3)
I'm trying to make a spec for a function that takes in a map with un-qualified keys (I know how to do this with qualified keywords, just use s/keys). The function arguments look like this: [{:keys [interest term balance] :as m}]
, and for that I make a function spec with (s/cat :m (s/keys :req-un [::interest ::term ::balance]))
- I have specs named ::interest
,`::term`, ::balance
. Is there a more direct way to make a s/fdef
spec for :args
? I don't use the whole map (`:m`) in the function body and only added that part to use in the spec. I tried the example in the clojure.spec documentation for s/keys
with unqualified keywords but couldn't get it to work. Alternatively, am I just way off by using un-qualified keywords as keys in the map the function takes?
the :as m
in the function argslist shouldn’t matter to spec, you should be able to remove it if you’re not using it
Thanks for answering so fast. So s/cat
needs key/pred argument pairs, so if I remove :m
from the definition of the function spec, what would I put instead?
the :m
in your function spec doesn’t have any relation to the :as m
in your arglist, it could be named anything really
s/cat
requires each “element” be tagged with some keyword though, so you can’t omit it. The actual keyword name doesn’t matter so much unless you’re interested in the conformed version of it i.e. it could be (s/cat :tgif ::map-spec)
and it’d still work
if it doesn’t work when you remove :as m
from the fn arglist, something else must be wrong
You are totally correct - took out :as m
in the function args list, still works. Can change :m
to any keyword (e.g. :foo
) in the :args
spec and that works too. But that's so weird! In this usage, I guess spec doesn't use the keyword arg for this spec?
That's weird. I wonder if there'll be other functions coming in spec like s/cat
that don't have that setup, it seems weird that for this case (which seems common?), it doesn't matter what keyword you put there - just a placeholder. Thanks!
s/cat
does use the keyword to tag conformed outputs, it just doesn’t matter for your use case
(s/def ::opts (s/* (s/cat :opt keyword? :val boolean?)))
(s/conform ::opts [:silent? false :verbose true])
;;=> [{:opt :silent?, :val false} {:opt :verbose, :val true}]
example from spec guide, notice the output has :opt
and :val
“tags”I guess the reason I didn't understand is I don't quite get s/cat and the other regex ops in spec.
@jamesvickers19515 So your function has one arg, a hash map? (s/cat :m ::account)
perhaps, with (s/def ::account (s/keys :req-un [::interest ::term ::balance]))
Thanks @seancorfield. @taylor showed me that when using s/cat
in this instance, the first argument actually didn't matter - could be :foo
for all it mattered.
Right, s/cat
takes a sequence of (whatever) argument names and specs.
It's convention to use the same (keyword) name for each argument as the function but there's no reason to.
In your case, you have a destructuring as the first (only) argument so its name is somewhat arbitrary anyway.
I guess what was surprising was that there wasn't a spec function for this case that doesn't require the unused keyword as the first arg - I was under the impression that functions that take a single map were common in Clojure.
Yes... not sure what you're asking...
Sorry, wasn't a question 🙂 just a comment
(s/cat ...)
is how you specify an argument list.
Specs name things, as part of their conformance. The names don't have to correspond to anything in the source code (`s/or` is a good example, s/cat
is similar).
I think I sort of see now. I did something like this at the REPL:
(s/def ::account (s/cat :m (s/keys :req-un [::interest ::term ::balance])))
(s/conform ::account [{:interest 4.25 :term 360 :balance 261250}])
=> {:m {:interest 4.25, :term 360, :balance 261250}}
Yeah, argument lists are sequences. s/cat
matches a sequence of (named) specs. So, in this case :m
is the name and the s/keys
is the spec for it.
And this call to valid? with a vector that looks like the function signature:
(s/valid? ::account [{:interest 4.25 :term 360 :balance 261250}])
=> true
Right.
Thanks, I think I understand s/cat
better after playing with it in the REPL a bit.