This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-09-21
Channels
- # alda (1)
- # bangalore-clj (1)
- # beginners (7)
- # boot (88)
- # carry (2)
- # cider (8)
- # cljs-dev (60)
- # cljsjs (2)
- # cljsrn (45)
- # clojure (255)
- # clojure-belgium (5)
- # clojure-boston (1)
- # clojure-dusseldorf (3)
- # clojure-greece (49)
- # clojure-italy (10)
- # clojure-russia (30)
- # clojure-spec (78)
- # clojure-uk (11)
- # clojurebridge (1)
- # clojurescript (80)
- # cursive (14)
- # datomic (33)
- # defnpodcast (4)
- # devcards (2)
- # dirac (15)
- # editors (23)
- # emacs (5)
- # events (11)
- # funcool (1)
- # hoplon (1)
- # juxt (1)
- # luminus (2)
- # mount (7)
- # off-topic (15)
- # om (152)
- # om-next (2)
- # onyx (17)
- # parinfer (1)
- # proton (38)
- # re-frame (35)
- # reagent (110)
- # rum (3)
- # spacemacs (3)
- # specter (51)
- # test-check (2)
- # testing (5)
- # untangled (234)
Hey folks, if I had a key in a map that I want to constrain to be a function that conforms to a spec, how would I go about that? I thought I would be able to pass the results of s/fdef
to (s/def ::my-key …)
but that does not seem to be the case.
Oh jeeze, missed that one thanks
It returns the result of def not fspec
Fdef is def + fspec
ah, so you could use (s/spec ::my-key) @alexmiller ?
Same as def but the key is a symbol not a keyword
It registers, not defines
Or at least that's how I think about it
oh, so you could use the symbol 'my-function in place of the spec? or at least (s/spec 'my-function)
Yes - fully qualified though
So back tick
user=> (defn foo [x] x)
#'user/foo
user=> (s/fdef foo :args (s/cat :arg any?) :ret any?)
user/foo
user=> (s/def :foo/bar user/foo)
:foo/bar
user=> (s/valid? :foo/bar foo)
true
user=>
hah, I can tell that changed it because valid? takes longer to return because of the generative checking of functions
damn, so a lot of the core functions take a single arity it turns out, but anyway yes
oot.user=> (s/def :foo/bar 'boot.user/foo)
:foo/bar
boot.user=> (s/valid? :foo/bar foo)
true
boot.user=> (s/valid? :foo/bar clojure.string/replace-first)
false
@alexmiller some feedback. I created a generator that did not conform to the spec (doh!). The generator contained the such-that predicate. When I tried creating a sample from the generator I got this error:
ExceptionInfo Couldn't satisfy such-that predicate after 100 tries. clojure.core/ex-info (core.clj:4725)
I assumed that it referred to my custom generator but that was a red herring because in fact spec must be using such-that to ensure that the generated value conforms to the spec, and it was this such-that that generated the failure, not the one in my custom generator.
Code (with the problem corrected but showing the such-that in my generator:
(defn mod11-checkdigit
"Calculate the checkdigit see "
[n]
(let [x (->> (map #(Integer/parseInt (str %)) (take 9 n))
(map * (range 10 1 -1))
(reduce +))
y (mod x 11)
c (- 11 y)]
(cond (== 10 c) nil
(== 11 c) 0
:else c)))
(def nhs-number-gen
"Generates a valid NHS number"
(gen/fmap #(str (+ (* 10 %) (mod11-checkdigit (str %))))
(gen/such-that #(mod11-checkdigit (str %))
(gen/choose 100000000 999999999))))
(defn nhs-number?
"Returns true if passed a valid nhs number else returns false"
[n]
(and (string? n) (= 10 (count n)) (= (str (mod11-checkdigit n)) (str (last n)))))
(s/def ::nhs-number (s/with-gen nhs-number?
(fn [] nhs-number-gen)))
It would be nicer if the error thrown due to the generated value being non-conformant with the spec stated this.
Say I have a namespace with two functions create-component-a
and create-component-b
. Each function takes a map with options. How would I go about spec'ing those option maps in the same namespace as the functions? Let's say that each map takes an :id
(or something else) attribute, but they're semantically different. If I want to use the ::keyword
shortcut to create a namespaced keyword, I can't call them both ::id
. I could create a namespace for each type of option map, alias them and use something like ::a-options/id
and ::b-options/id
, but that seems wrong.
@kestrel7 you are correct in your assessment - spec doesn’t trust custom generators and re-confirms they match the spec. Agreed that it would be nice to know the cause here more explicitly (kind of a special case of the general desire to know more about which part of a generator failed to generate). Happy to see a jira for enhancement.
@patrkris your last suggestion is what I would do there (you don’t have to alias of course - you can just use fully-qualified too)
why does that seem wrong?
@alexmiller: I don't know. Maybe it shouldn't 🙂. It's probably just from being used to define a schema (with herbert) in the same namespace as the function relying on it. But using the fully qualified version also feels a little wrong, because then you have hardcoded the namespace. It possibly just something I need to get used to
I thought about whether allowing the use of "subnamespaces" in aliases would be useful. For instance ::a-options/id
without having an ::a-options
alias. But I can imagine that it would turn out to be problematic.
Ehr... by "subnamespaces" I meant being able to say ::order.delivery/type
without having a order.delivery
alias created
well, I don’t think we’ll do that :)
@alexmiller as requested http://dev.clojure.org/jira/browse/CLJ-2025 - thanks for the great work.
Is there a way to require that in {:a "a" 😛 "b" :c "c"} :a is required and 😛 or :c is required and still have the other be optional? I've tried this using s/keys but cant figure out how i'd do it and I can't see how i could use s/map-of and still require :a...
oh, I was just thinking. how is that working as clojure.core/or ... guess it's not haha
Some functions, like exercise
, allow you to override generators by name. Is there any mechanism to do the same with conformers?
@alexmiller I think a good solution to aliasing keyword and symbol namespaces not existing in the codebase like :human.programmer/language
would be (defalias hp human.programmer)
. So :hp
would still be :hp
, but ::hp/language
would resolve to :human.programmer/language
. This would overwrite aliases created via require
. Should I write a tickent?
@leongrapenthin alias
already exists
the problem with it is that it requires the ns you are aliasing to exist
we are already considering loosening that constraint or auto-creating the ns
I don’t need a ticket for it
interesting. auto-creating seems like a good strategy
that is by far easier given the existing implementation of Namespace etc
I just realized that I could use create-ns in combination with alias
So there is a good workaround
yes that works fine now
DK about Cljs though
have you looked at multi-spec?
has that kind of a feel to me
I keep running into the same problem where I have two maps that have the same named key, but with different specs. For example: two maps that both have a key called name
, where in the first it is a string?
, but the second is int?
because it is more of an ID. How do I specify this so that the specs can be different, but the field name the same in both maps? Only solution I see is to change name
to something else (like person-name
) and just living with the name change, but that doesn't work especially if modeling a domain I don't control. Thoughts?
@manderson :opt-un and :req-un in s/keys
Not sure I'm following... I still have to define a spec called ::name
in two different ways. Yes, I could use or
, but that muddies the definition since they are actually separate specs.
@manderson sorry I should've been clearer, define a spec called :my.app.person/name and a spec called :my.app.service/name or whatever makes semantic sense
Ok, so using the namespacing. That makes sense and should work for most of my cases (often translating from a format like JSON where there aren't namespaces). I suppose in the case where namespaces are expected, it would probably already be modeled correctly from the beginning (hopefully). Thanks @bfabry
@manderson To (hopefully) clarify: if you’re spec’ing with :req-un
and :opt-un
then you are using simple (non-namespaced) keys in your maps, but you can use a namespaced spec name / key name there to distinguish the two specs.
(s/def ::person (s/keys :req-un [:person/name]))
(s/def ::service (s/keys :req-un [:service/name]))
Both person and service specs are maps with a required :name
key, but the specs used to validate those keys’ values will be different for ::person
and ::service
— does that help?(it was something that I did not get on the first two or three reads of the Guide and it was Alex that set me straight on that)
Hi I am beginning a large multi-developer project that will use spec extensively I was wondering if there is any advice for how to place specs in namespaces. I was thinking of having a namespace structure like
project-name.spec
-common
-thing1
-thing2
...
and then requiring these namespaces in the actual code, probably when doing s/fdef
below the defn
this will require lots of map access to be ::thing1/id
or ::thing1/date
etc
is this a good idea or should I be trying to keep all specs within the namespaces they deal with so ::id
can be used when possibletying your data model (names of attributes) to the way your code is currently organized seems like a bad idea