This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-12-19
Channels
- # beginners (240)
- # boot (9)
- # braveandtrue (2)
- # bristol-clojurians (2)
- # cider (2)
- # cljsrn (84)
- # clojars (1)
- # clojure (195)
- # clojure-belgium (9)
- # clojure-china (5)
- # clojure-denmark (4)
- # clojure-italy (7)
- # clojure-mke (1)
- # clojure-norway (1)
- # clojure-russia (16)
- # clojure-spec (74)
- # clojure-uk (15)
- # clojurescript (78)
- # clr (3)
- # code-reviews (4)
- # datascript (8)
- # datomic (71)
- # emacs (9)
- # hoplon (18)
- # jobs (3)
- # kekkonen (32)
- # klipse (19)
- # lambdaisland (2)
- # luminus (15)
- # off-topic (6)
- # om (35)
- # om-next (62)
- # onyx (17)
- # overtone (5)
- # pedestal (1)
- # perun (1)
- # planck (31)
- # protorepl (1)
- # re-frame (135)
- # reagent (34)
- # ring-swagger (6)
- # rum (54)
- # specter (3)
- # untangled (14)
- # yada (14)
I recall reading advising caution on using conform
awhile back. Is this still the case?
sometimes that’s useful to make a spec work, but it’s usually better to do that work in a function rather than a spec
ie just translate the data and spec the data that exists before and/or after that translation
okay cool, so there’s no problem with conform
then in both the latest cljs and clojure right?
perfect, gonna save me a non-trivial amount of time writing destructuring branches on my multispecs 😉
Paraphrasing Alex: if your spec also coerces data, bear in mind that this coercion will apply to all clients of that spec.
At World Singles, we have a lot of specs that coerce string data to typed data. We’re OK with that 🙂
Specifically, we have specs for our API calls that accepts strings as input and the result of conforming them is domain model values (so string -> long in some cases and string -> keyword in others).
We generally write the specs for the domain model values first, and then write the API specs in terms of those (so an API spec is (s/and string? (s/conformer some-coercion) ::domain/spec-name)
and we have a generator that fmap
s the reverse coercion over the ::domain/spec-name
(either str
or name
usually).
We have some shorthand coercion specs tho’ (e.g., ->long
which does (try (Long/parseLong s) (catch Exception _ ::s/invalid))
).
i think what seancofield is saying, and by extension alex, is that conformer couples some interpretation of the data to the specification - however somebody else may want a different interpretation. so if you choose to use conformer, you prevent yourself and others from being able to apply a different interpretation
if you don’t expect to ever need another interpretation, then go right ahead, but just know you’re making that trade off
seancorfield: the other option would be to not use conformer and then to do a secondary pass where you apply coercions, right?
i’ve been hacking on this thing: https://github.com/brandonbloom/ambiparse and i made a decision up front to include semantic rules as a primary feature - it’s basically the same idea as conformers
but parsing is different than validation - in parsing, you’re pretty certain to have a lot of “trivia” like whitespace etc that you want to discard
@bbloom My objection to that is that you end up doing the work twice: once to determine whether a value is valid and then again to actually convert it — since you always want to convert it if it is valid.
I mean, you could use a regex I suppose to say “It’s valid if it’s just digits and not longer than N” but that’s not the same as actually parsing a string to get a number — you’ll either allow too many digits or not enough.
And if you decide to use a regex, now you have two problems 🙂
sure, the guidance is essentially “consider if you might want to NOT convert it in the future” - and if the answer is “nah, i’ll never need not do that” then go right ahead
And it’s easy to write a non-coercing version of these specs: you just call str
or name
on the conformed result 🙂 (s/and ::api-spec/spec-name (s/conformer str))
🙂
or you can use something like https://github.com/greglook/whidbey
yeah i think it’s fine, it’s just pulling out the common namespace part of the keywords but I guess this is normal behavior for pprint
@naomarik yes that's new syntax for namespaced keys in maps (namespaced maps?), I think it has been added in the first 1.9-alphas
. They will all print that way
ah ok the bot is not on 1.9
yet
(get #:test{:foo 1 :bar 2} :test/foo)
1
Do specs belong with my application code or should I put them in their own namespace? Also, should I assert within application code or use the :pre and :post keywords to check my function i/o?
i asked the same question about where to put specs - it seems like both options occasionally make sense
it seems like if you have a single-namespace that wants to export some specs, go right ahead and put them inline
if you have a bunch of namespaces collaborating around some common stuff, maybe put the specs in their own namespace
the only decision you really need to make up front is what namespace you want them to land in in terms of usage externally
there’s also a risk of a circular dependency between the spec namespace and the other namespaces
@kyle_schmidt As for assert / :pre / :post — “it depends”.
I would normally turn on instrumentation as part of my tests, and have certain tests that run check
on key functions (ones that can be generatively tested). I generally do not have assert
in any production code and only very rarely have :pre
or :post
.
Thank you for the replies. And just to make sure, is clojure.spec a replacement for clojure.test? Or would I still want to write both?
oh interesting. so use specs to aid tests
Ah, I interpreted that question as being whether clojure.test.check
replaces clojure.test
.
@kyle_schmidt: there’s a bunch of spec videos by stu halloway on youtube that are a good intro of actually doing some testing w/ spec
cool thanks I’ll check them out!
Is there a way to spec out a function as part of a collection, such as a map? For example, if I define a spec like:
(s/def ::callback (s/fspec :args (s/cat :x number?) :ret number?))
(s/def ::foo (s/keys :req-un [::callback]))
Calling (s/valid? ::foo {:callback inc})
returns false
I think in practice, I would typically just spec out the function I was using as the callback itself, and spec the key as just fn?
. It seems simpler, but I wasn't sure if I was thinking about this correctly
@lambdahands I get true
for that valid?
call
yeah, I would expect that to work
Hmm. I am using clojure-future-spec – let me try with the latest alpha as well.
(also ifn?
is usually better than fn?
)
Ah, thanks for the tip @alexmiller!
@naomarik you can also turn off namespace map syntax for maps if you want (set! *print-namespace-maps* false)
it will default to false in Clojure but true in the repl
Checked out that spec again, and it does work as you said. Strange, I must have been doing something wrong earlier as I was getting an unexpected result.