hey folks, excuse my noobiness, but is it possible to return a s/def from a function?
I'm trying to build them dynamically from a parsed json which is working okish to the point where I try to return them
s/def is a function which I don't think you mean. do you mean a spec form or a spec object?
Ah, yes. Sry, thatโs what I mean. I want to create them dynamically, save their names to an atom and then do an s/def
one way you could do this is to directly manipulate the registry (as s/def's impl does). in spec 2 we've pulled out a non macro function s/register for this purpose.
(defn len [n] (and (< (count n) 6)
string?))
(s/def ::piece-IDs (and vector?
(s/coll-of len into [])))
(s/valid? ::piece-IDs ["92257" "12" "01234"] );; => true
(s/conform ::piece-IDs ["92257" "12" "01234"]) ;; => ["92257" "12" "01234"]
(s/explain-str ::piece-IDs ["92257" "12" "01234"]) ;; => "Success!\n"
In the above code the s/def is valid, however it depends on the defn provided - seems untidy and not best practice
Can this not be changed to incorporate the count condition soemwhat as below?
(obvs. the coud does not work as the count does not apply to the strings)
(s/def ::piece-IDs (and vector?
(s/coll-of #(and (< count 0)
string?) into [])))
Any ideas to eliminate the defn in the above case?you don't need a custom predicate for that at all
(s/valid? (s/coll-of string? :kind vector? :max-count 5) ["1" "2" "3" "4" "5" "6"])
coll-of
has a :max-count
modifier
also note that in spec 2, there is now a catv
too that would slim this down to just (s/catv string? :max-count 5)
Hi, @vlaaad and @alexmiller
(s/def ::pieceIds (s/coll-of string? :kind vector? :max-count 5))
(s/valid? ::pieceIds ["JD014600007821392257" "12" "01234567890123456789" "777777777" "66666666666666666666666666666" "77"]) ;; => false it is counting 6 strings in vector
(s/conform ::pieceIds ["JD014600007821392257" "12" "11111111111111112222222222222222222y"]) ;; => ["JD014600007821392257" "12" "11111111111111112222222222222222222y"]
(s/explain-str ::pieceIds ["JD014600007821392257" "12" "11111111111111112222222222222222222y"]) ;; => "Success!\n" it is not applying the string length limit of 5 chars
produces the count how many string there are; whereas the code I presented determines whether every string length is less than 6 (this being the challenge I find myself in)
Subtle difference to what I suggested. Any ideas?
BTW Alex: I am using leiningen so I am not sure how to exactly refer spec 2; i.e. I do not use an edn file per se. Perhaps some guidance on that would be helpful - It refers to git etc. so I am unsure
@dev.4openid Do you want a :min-count
as well as a :max-count
?
Oh, you want the strings themselves to be checked for length, not the vector?
Yes
So you want (s/def ::short-string (s/and string? #(>= 5 (count %))))
and then (s/coll-of ::short-string ...)
:min-count
and :max-count
apply to s/coll-of
, not to things inside the collection.
Well, it looks like I had the general idea right ๐ but not the implementation. I will try now
@seancorfield No it must be missing something
(s/def ::short-string (and #(>= 5 (count %))
string?))
(s/def ::piece-IDs (s/coll-of ::short-string into []))
(s/valid? ::piece-IDs ["92257" "12" "012344444"] );; => true NOT
(s/conform ::piece-IDs ["926257" "12" "01234"]) ;; => ["92257" "12" "01234"]
(s/explain-str ::piece-IDs ["9992257" "12" "01234"]) ;; => "Success!\n"
Read what I suggested for ::short-string
and compare it to what you wrote.
(I just edited mine BTW)
(and #(..) string?)
is truthy
(s/and #(..) string?)
is a spec
Also, it's safer to add the type predicate (e.g., string?
) first before applying count
otherwise this will blow up (s/valid? ::piece-IDs [42])
because it will try to call (count 42)
before testing it is a string.
Yep, I missed it ๐ now it works and I have to discipline myself on the s/. Bit of improvement work!! @seancorfield thanks for the help. Will use string (type checks first) Thx
(somewhat ironically, my incorrect version -- (and string? #(>= 5 (count %)))
-- worked by accident because it evaluated to just #(>= 5 (count %))
because (and truthy x)
=> x
for any x
I only realized my version was broken when I tried the 42
example and mine blew up, even tho' it had correctly rejected your test example with "012344444"
I am learning this language ad enjoying it. this is after the last serious coding I did 30 years ago. It is a steep curve, it feels like doing "maths" all over again - strict discipline and be aware of the subtilties . There are so many variations and tweaks I sometimes get lost! Too easy too make mistakes. Worth learning as it is pretty powerful. Thx for your help
@dev.4openid The REPL is your friend here. If you try out every single function as you write it, you might have caught the problem with your len
function sooner:
user=> (defn len [n] (and (< (count n) 6)
string?))
#'user/len
user=> (len "123")
#object[clojure.core$string_QMARK___5410 0x3bc735b3 "clojure.core$string_QMARK___5410@3bc735b3"]
user=>
and:
user=> (len 42)
Execution error (UnsupportedOperationException) at user/len (REPL:1).
count not supported on this type: Long
user=>