On Tuesday, October 11, 2016 at 1:30:18 PM UTC-5, plamen.use...@gmail.com wrote: > > Hello, > > I have a problem which is probably not in the spirit of clojure.spec as > being a library for "only" checking/generating valid values, but of > substantial practical value for my use case: > > Let say I have a function for checking if a double precision number is > parsable from a string (where possible performance penalties because of > communicating non-parsable strings through try/catch in the implementation > is not part of my question): > > > (defn str-parsable-double? [s] > "Note the IMPLIED transformation from string to a double" > (try > (Double/valueOf s) > (catch NumberFormatException e > false))) > > > It's not detrimental here (other than this fn won't have the intended docstring), but this code has the docstring in the wrong place - it goes before the arglist, not after.
> and have a spec using this function: > > (s/def ::str->double str-parsable-double?) > > > We could actually chain checks for a single value as in: > > (s/def ::str->double (s/and string? str-parsable-double?)) > > This is a good practice. In general most specs will not generate automatically unless you start from a type-based predicate so this particular part I think is very much in line with good practices. > and we could check for the conformity of a value as per: > > (s/conform ::str->double "3.14") > => "3.14" > and > (s/conform ::str->double "Pizza") > => :clojure.spec/invalid > > Until now everything is ok, now my the questions: > > In my real world example I need a chain of a combination of checks and > transformations in the following way: > ::check1 ::check2 ::check-and-transform3 ::check-4-on-transformed-value > ::check-5-on-transformed value > or something like > (s/def ::transforming-check (s/and string? ::str->double ::bigger-than-zero? > ::smaller-than-pi ... etc ...)) > > As the result of > (s/conform ::str->double "3.14") is the input value as a string instead of > the result, > > I would need implement ::bigger-than-zero? and the rest of the checks as operating on strings, which would mean that I need in each check to transform the string into a double, which in my case is for performance reasons bad. > > On the other side I would be able to do the checks in a 2 step fashion, which > goes against my understanding of the aim of clojure.check to be able to > create through composition a single set of validations for a single value. > This also doesn't solve fully the problem with the multiple parsing of the > first data type before the transformation: a parsing happens in the > ::str->double step as well in the then explicit transformation step before > the final checks based then on the actual double. > > You can use s/conformer for this, although I will share the caution that use of s/conformer in your registered specs means that you are obscuring (and throwing away info) from the input value for your spec consumers. This is not inherently bad, but it is a tradeoff that should be considered when mixing validation and conversion. You could keep the conversion out of the spec and do that via a transformation or via a non-registered spec too. I can't say which of these is "right" for you, but you should at least think through these things ahead of time. A generally good practice is to also supply an unformer function with your conformer (here, probably just str would work) - this would allow you to run the spec backwards with s/unform and retrieve the original value. > So - is there a way to compose checks in a single one, where some of them > change the underlying type of the value for subsequent validations (I may > miss something in the Clojure documentation, and pointers to it would be very > welcome)? > > If not - why s/conform returns the input value instead of the result value? > As being on the caller site of s/conform I know what I passed and I know what > s/conform would give me in the negative outcome of the validation. > If s/conform while iterating/recursing through the actual spec would apply > the checks on returned values of previous checks instead always on the input > - the problem would be solved, without negatives for the calling sites (as > because of the previous sentence). > This could be solved for example (not sure if I see every detail yet, but at > least as a pointer) in that in the clojure.spec code > > Spec > (conform* [_ x] (let [ret (pred x)] > (if cpred? > ret > (if ret x ::invalid)))) > the value of cpred? could be passed from the calling site (or if don't miss > something be even per default true). > > In any case, I would be thankfull for any suggestions solving both proplems: > how to express such checks in a good way and how to avoid reparsing values > again and again (where the current example is for just strings to doubles, > but the actual problem could involve any transformations on the value incl. > type). > > With best regards > Plamen > > > > > > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@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+unsubscr...@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+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.