--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
FWIW, I’ve been using https://github.com/metosin/spec-toolson a couple of projects lately, which helps nicely withconformance and coercion. The main devs are very helpful on #ring-swagger on the Clojurians slack.Alex, how does spec-tools measure up to your thoughts on conformance/coercion?
Out of curiosity, are you at liberty to discuss how Cognitect solves the problem of validating/coercing values at the edges of the application when you (Cognitect) are doing consulting?
Calling s/valid? will tell me if the data is valid *if it has been conformed*. But what if it hasn't? Can I use the data? Is it "valid" according to the spec I wrote?
If your spec includes coercions, you have inherently made the “is valid?” question include the coercion. Your ::test-spec accepts anything that can be used as the argument to keyword. This is why Cognitect keep recommending people do not do this. And I’m jumping into this thread because we _do_ include coercions in some of our specs at work… we’ve been heavy users of spec since the early alpha builds and we ran those alphas in production. But we are very conscious about our specs that coerce: we know and accept that they will work on “any string that can be coerced to <desired type>”. If we specifically want to check whether some data is <desired type> we use a different spec.
We do this for parameters in our REST API, for long, double, Boolean, date, etc – we have two specs for each: one that is a spec for the target type in the domain model (which in these cases is just defined as the appropriate built-in predicate), and one that is a spec for the API level (which accepts either the target type or a string that can be coerced to the target type). Then we use the appropriate spec at the appropriate “level” in our application.
Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood
On Tuesday, February 20, 2018 at 10:28:12 AM UTC-6, Erik Assum wrote:FWIW, I’ve been using https://github.com/metosin/spec-toolson a couple of projects lately, which helps nicely withconformance and coercion. The main devs are very helpful on #ring-swagger on the Clojurians slack.Alex, how does spec-tools measure up to your thoughts on conformance/coercion?spec-tools combines specs for your desired *output* with a coercion function, making the spec of the actual data implicit. I feel like this conflates too many things and obscures the actual input data value, which is the same problem the original poster had. Also, spec-tools introduces the notion of "type". spec intentionally avoids creating a new vocabulary of special words like this and in all cases relies on predicates or things mapped to predicates instead. I'm not a fan of this approach and I don't like the idea in CLJ-2116 much either - I think it's pretty unlikely this is where we will end up.
In general, I think a lot of the functionality in spec-tools either uses implementation internals that are almost certain to break as spec evolves or is at odds with the philosophies of spec as stated in https://clojure.org/about/spec (like the type vocabulary thing).
I do see the problems driving this, and I agree there is a gap to be filled here though.
This is exactly why we recommend that you not use conformers for coercion. Conformers were added primarily as a tool for building custom composite spec types (for example, we used it to build keys* from keys).
This is a common need though and I would be happier if spec did more to help you solve it in a way that minimized repetition and maximized the use of existing specs. I'm still thinking through what that would mean exactly. It's challenging right now to plug externally without rebuilding a significant part of spec, so that's obviously not ideal.Ideally, you would be able to say the things that are important here:- the spec of the incoming data (strings or whatever - JSON sourced is the major use case)- the spec of the data I desire- the coercion functions that can move from one to the other (there are probably a small number of these that are widely reused)- some way to coerce+validate or coerce+conformBuilding coercion into a single spec itself instead leads to the problem of not being able to know what the source data actually was, and that's really at odds with the spec philosophy and the notion of bidirectional conforming.
Looking around I see lots of cases where people do use conformers for coercion.
That doesn’t make them right 😊
At a first glance it seems very natural, and warnings not to do it are not easily found.
Every single time coercion comes up anywhere in the context of spec, someone says “don’t do that”, and they’ve been saying it since the earliest alpha versions of spec. You would be correct to point out that nothing in the spec overview or spec guide on clojure.org carries this caution, however (and I think it’s a reasonable “ask” for the guide to be updated to include such a caution).
My recommendation is to have a strictly non-coercive spec for the target data “type” / shape you want, and to have a second spec that combines the coercion you want with that spec. That way you have a way to tell if your uncoerced data conforms to the spec, as well as a way to do coercion in s/conform. They are – and should be – two separate specs and two separate operations. They represent different layers of abstraction inside your application (so “of course” they should be two separate specs, one built on top of the other).
Given that the overview and the guide don’t even mention s/conformer, I’m not sure where that recommendation should live. Alex, any thoughts on this, since you seem to be the one most often making the recommendation?
Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood
I would actually love it if Spec was extended to have the concept of types.
Something where every spec could be tied to a Type, and types could be constructed to have Hierarchies.
Looking around I see lots of cases where people do use conformers for coercion.
That doesn’t make them right 😊
At a first glance it seems very natural, and warnings not to do it are not easily found.
Every single time coercion comes up anywhere in the context of spec, someone says “don’t do that”, and they’ve been saying it since the earliest alpha versions of spec. You would be correct to point out that nothing in the spec overview or spec guide on clojure.org carries this caution, however (and I think it’s a reasonable “ask” for the guide to be updated to include such a caution).
My recommendation is to have a strictly non-coercive spec for the target data “type” / shape you want, and to have a second spec that combines the coercion you want with that spec. That way you have a way to tell if your uncoerced data conforms to the spec, as well as a way to do coercion in s/conform. They are – and should be – two separate specs and two separate operations. They represent different layers of abstraction inside your application (so “of course” they should be two separate specs, one built on top of the other).
Given that the overview and the guide don’t even mention s/conformer, I’m not sure where that recommendation should live. Alex, any thoughts on this, since you seem to be the one most often making the recommendation?
Map specs are about attribute aggregation, not about types.
You can effectively do this already. When you have s/keys maps which are sets of attributes, you can simply combine sets (base and extensions) using s/merge. Or when needing more polymorphism, s/multi-spec.
(defmacro def-derive "Macros to wrap useful pattern of defining a spec and calling derive on the spec and a \"parent\" spec to create a hierarchy." ([child-name parent-name] `(def-derive ~child-name ~parent-name ~parent-name)) ([child-name parent-name spec] `(do (#?(:clj clojure.spec.alpha/def :cljs cljs.spec.alpha/def) ~child-name (#?(:clj clojure.spec.alpha/merge :cljs cljs.spec.alpha/merge) ~parent-name ~spec)) (derive ~child-name ~parent-name))))