This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-09-30
Channels
- # bangalore-clj (1)
- # beginners (9)
- # boot (51)
- # cider (20)
- # cljs-dev (419)
- # clojars (1)
- # clojure (338)
- # clojure-brasil (64)
- # clojure-dev (7)
- # clojure-greece (2)
- # clojure-italy (3)
- # clojure-russia (10)
- # clojure-spec (127)
- # clojure-uk (12)
- # clojurebridge (2)
- # clojurescript (132)
- # core-async (8)
- # cursive (37)
- # datomic (34)
- # dirac (5)
- # events (1)
- # funcool (3)
- # hoplon (39)
- # jobs (3)
- # leiningen (3)
- # off-topic (16)
- # om (44)
- # onyx (7)
- # pedestal (20)
- # protorepl (1)
- # random (1)
- # re-frame (64)
- # reagent (6)
- # specter (4)
- # test-check (9)
- # untangled (17)
- # vim (4)
hey guys… anyone here using spec and prop. based testing in clojuresript? I have encountered strange problem. (s/gen)
within a test that works perfectly on my machine, on CI machine (same java, lein, node and phantomjs versions) says ""Couldn't satisfy such-that predicate after 100 tries.”,
it’s not like random. I run it many times. it passes on my machine and it ALWAYS fails on CI machine
what kind of spec is it?
oh… oh… it’s a bit nasty… I need to generate iso-date strings and I did this:
(s/def :date/iso (s/with-gen (s/and string?
#(re-matches iso-date-regex %))
#(gen'/for [d (gen'/datetime)]
#?(:clj (time-f/unparse local-date-formatter d)
:cljs (.format (js/moment (js/Date. d)))))))
I know this has probably been asked 1000 times but what is the prevailing preference for where to put specs? In their own parallel namespace or inline?
my impression is inline for function specs, different namespace for “domain” specs
It depends.
But yeah, I think @stathissideris comment reflects what is becoming the most likely best practice at this point. It's what we're doing at World Singles so far...
and "domain" spec means specs which are likely to impact more than one namespace and be reused a lot?
Data structure specs, as opposed to function specs.
Know of any open source projects that are already using it, that I could take a look at?
clojure.java.jdbc
? 🙂
Thanks. Looks like they follow the some.namespace and some.namespace.spec convention instead
I was thinking an advantage of in-lining is that it provides some documentation, but am I right that spec adds something to the docstring anyway?
s/they/yourself/ i see
Ok, I might be leaning towards a separate namespace in that case
Inlining fdef makes sense for documentation, but not for portability across versions. def for data structures in a separate namespace makes sense regardless.
We've generally found if we start to def a data structure spec in a namespace with functions, it's gets messy ... keeping the data structure specs separate is more flexible...
I'm worried things will be messy with function specs too, it seems the documentation advantage is somewhat negated by the docstring
Not sure what you mean...? Specs just add to the docstring...
I mean that if it's added to the docstring then it isn't much of an advantage having it inline as well.
Right, yes, but the docstring is only updated if you've loaded the ns with the spec so you might as well have the fdef inline (IMO)
yeah.. different documentation requirements. documentation for people reading/maintaining the code, documentation for people using the function
Hi, I am trying to use spec to conform the arguments of a function: there is at most 4 arguments with 4 distinct types. I want to conform them into a map but the solution I came up with is not fully compliant because it allows 2 arguments of the same type. Is there a way to make sure there isn't 2 arguments of the same type ?
Not sure I follow... can you share some code?
@seancorfield yes sorry you are too quick 😄
Your coll-of
is a map so each :message
item overwrites any prior item before :distinct
can be checked.
And if you used :into []
you would still accept those two :message
arguments because they are different values so they are distinct.
The :distinct
flag says that all the conformed values must be distinct -- which they are.
You'd need your ::argument
spec to conform to just the type, not the type and value.
Hmm, that doesn't quite work either:
(s/def ::argument (s/and (s/or :message string? :app-data map? :type keyword? :variables vector?) (s/conformer first)))
(s/def ::arguments (s/coll-of ::argument :distinct true :into [] :max-count 4))
(s/conform ::arguments ["helo {a} hello" "hello" ::random-log {:c '(inc c)}])
[:message :message :type :app-data]
Not what I expected.@seancorfield if I have 2 identical arguments the predicate distinct? is triggered so the distinct check happens before the into {}
however this not really what I want I could remove it, what I would like is to have distinct types for the arguments not distinct arguments
Yeah, I think you're right about the :distinct
check... it would check distinct values before the conforming.
there's something from a very long time ago whispering in my ear that this is an irregular grammar, and so you're going to need a custom predicate to validate it. but I could be wrong
So you need s/and
on ::arguments
to force it to be distinct types.
(s/def ::arguments (s/and (s/coll-of ::argument :into [] :max-count 4) (s/coll-of keyword? :distinct true)))
(s/conform ::arguments ["helo {a} hello" "hello" ::random-log {:c '(inc c)}])
:clojure.spec/invalid
(s/conform ::arguments ["helo {a} hello" ['b] ::random-log {:c '(inc c)}])
[:message :variables :type :app-data]
And that's with
(s/def ::argument (s/and (s/or :message string? :app-data map? :type keyword? :variables vector?) (s/conformer first)))
So each argument needs to conform to its type (throwing away the value) and then the arguments collection needs to flow the conformed arguments into a distinct collection
wow thank you it works but it also makes me realize I still have a lot of work to do on spec 🙂
Yeah, it's radically changing how we approach several types of problems.
oh ok I understand now because I didn't realize that I don't get the values anymore while conforming
Took me a while too. conformer
uses its function argument to transform the data rather than just being a true/false predicate.
But Alex says it's an anti-pattern so be cautious.
well I'll play a bit to see if I can keep my conformed map and still have the validation on distinct keywords
btw do I misunderstand unform ? I thought this call (s/unform ::x (s/conform ::x data-to-conform)) would return data-to-conform but it just returns the conformed data
@seancorfield is the explanation on why it is an anti-pattern available somewhere ?
below is the final spec I made thanks to your input that matches my needs, I could eventually just do the map transform outside of the spec iff conformer is an anti-pattern
@ag Here's how I generate date strings:
(require '[clj-time.core :as t]
'[clj-time.format :as f])
(import '(org.joda.time.format DateTimeFormatter))
(defn parseable-timestamp
"Returns a spec for a timestamp string can be parsed with the specified
datetime formatter."
[formatter]
(s/and string?
(fn [timestamp-str]
(try
(f/parse formatter timestamp-str)
true
(catch Exception _
false)))))
(defn make-timestamp-gen
"Returns a generator for a timestamp string between the minimum year and
maximum year (exclusive) that can be parsed with the specified datetime
formatter."
[min-year max-year formatter]
(fn []
(let [year-gen (s/gen (s/int-in min-year max-year))
month-gen (s/gen (s/int-in 1 12))
day-gen (s/gen (s/int-in 1 28))
hour-gen (s/gen (s/int-in 0 23))
m-s-gen (s/gen (s/int-in 0 59))]
(->> [year-gen month-gen day-gen hour-gen m-s-gen m-s-gen m-s-gen]
(map #(gen/fmap vector %))
(apply gen/cat)
(gen/fmap #(->> (apply t/date-time %)
(f/unparse formatter)))))))
(def timestamp-formatter (f/formatters :date-time-no-ms))
(s/def ::timestamp
(s/with-gen
(parseable-timestamp timestamp-formatter)
(make-timestamp-gen 2000 2050 timestamp-formatter)))
asking again: I'd like to generate documentation from specs, is there a way to resolve/expand specs from (s/form ...) (or other) ?
I guess I can hack this walking the (s/registry) + (s/form ...), but that seems a bit "hairy"
(it's for front-end team, so I cant just spit keywords, i need to resolve the leafs of specs to stuff they understand)
are you talking about a “deep” version of s/form?
yeah, we’ve talked about providing that but it doesn’t exist right now
that'd be nice, it's should be easy enough to write, but that's something one would expect out of the box imho
I have specs for spec forms (kind of, modulo a number of bugs in s/form) which could help a lot
because you could conform the spec and then pick the parts per form that you need
I will probably get around to this soon-ish as I need it for other things
having specs for specs opens up all sorts of things
for example, automated testing of spec by generating specs from spec specs, then checking conformance on data generated for the generated spec
oh since you're here, I have another question about the conformer + :clojure.spec/invalid issue I mentioned yesterday
yes, it feels like that :)
can you repeat? I can’t keep up the slacks
yeah, that’s been logged
would have changed as of alpha 11 or 12
I mean changed as in “started failing”
I’m not sure there is a good solution to it
http://dev.clojure.org/jira/browse/CLJ-1966 is basically the same probelm
it makes writing conformers a bit tricky, I basically have to (def invalid :clojure.spec/invalid) and use this in the body of the functions I am using
yeah, but wait for a spec on def to break that too :)
it’s a broader problem
one possible solution would be to do something like is done in the reader where a reader is handed a token that it can return to indicate a special case
so rather than being expected to return ::s/invalid, you are given a token that you can return (and that can be a per-instance (Object.)
)
something like that
that still doesn’t solve the problems around core specs though
it needs a longer conversation with Rich and he hasn’t had the time to have it
whatever we fix or add next :)
CLJ-2024, CLJ-2027, and CLJ-2026 all have patches that are ready for Stu and Rich to look at so I expect those to be in the next alpha
(presuming they like them)
Actually walking the specs isn't really possible in my case: if you do (s/def ::foo ::bar)
(s/form ::foo) will have expanded ::bar to a predicate so I cannot check my stop point (since I don't want to expand down to the last bits, I have a set of specs that are the surface I want to expose to the front-end people)
i’ve got a somewhat complex map spec that i’m trying to s/exercise, and i’m getting the dreaded "Couldn't satisfy such-that predicate after 100 tries.” error. what are the primary things i should be looking for when tracking down the root cause?
is naive s/and
usage the primary trigger for this error, or are there other categories of spec misuse that often cause it?
(if it’s helpful, the spec i’m trying to exercise is https://github.com/jrheard/voke/blob/spec/src/voke/specs.cljs#L74 )
i’ve only got one s/and in here, and it’s a (s/and number? pos?)
, so i feel like there must be some other category of thing i’m doing totally incorrectly
this guy’s the culprit: https://github.com/jrheard/voke/blob/spec/src/voke/specs.cljs#L22
the documentation on :kind says: "Note that if :kind is specified and :into is not, this pred must generate in order for every to generate.”; and adding :into #{}
fixes the issue. guess i need to sit down and take some time to understand the semantics of :kind
and :into
. thanks all ❤️
(if you click on those links after coming back from lunch, pretend that the (s/coll-of)
calls with :kind
specified do not have a corresponding :into
; that’s the state the file was in when i linked it, and that’s what the problem was 🙂 )
@jrheard Late to your spec party but, yeah, naïve s/and
has been my primary trigger for that error — I’ve taken to using s/with-gen
quite a lot to "help" clojure.spec exercise stuff.
I s/fdefed a function but the doc string doesn't contain my spec. Did I miss something?
Is this the expected behavior?
(require '[clojure.core.specs :as clj-specs])
(def c (s/conform ::clj-specs/defn-args '(t
[x y]
x)))
=> #'boot.user/c
(s/unform ::clj-specs/defn-args c)
=> (t (x y) x)
Shouldn’t the args be unconformed to a vector, not a list?(let [args '(t
[x y]
x)
args-s ::clj-specs/defn-args
c (s/conform args-s args)]
(s/conform args-s (s/unform args-s c)))
=> :clojure.spec/invalid