On Wednesday, May 17, 2017 at 3:02:17 PM UTC-5, marian.hor...@vacuumlabs.com wrote: > > Hi, > > I am writing a function that transforms Specs to another formats (similar > to the JSON Schema). Assuming from this post > <http://www.metosin.fi/blog/clojure-spec-as-a-runtime-transformation-engine/>, > > I am not the only one. There is no surprise that I am using > clojure.spec/form. Unfortunately I am not fully satisfied with its > output. To illustrate the problem let's suppose that my transformer will be > used by a third person who like to write its own spec-generating helpers: > > (require '[clojure.spec.alpha :as spec]) > > (spec/def ::forgivable-keyword (spec/conformer (fn [x] (cond (keyword? x) x > (string? x) > (keyword x) > true > ::spec/invalid)))) > > (defn kw-enum [& enum-values] (spec/and ::forgivable-keyword (apply > hash-set enum-values))) > > s/and (and all of the spec forms) are macros to facilitate capturing the spec form for use in s/form and error reporting.
In this case, (apply hash-set enum-values) is not either a predicate or a set and thus is not a valid spec to use in s/and. I presume what you're looking for is the ability to dynamically create specs - for this, please consider using either eval or a macro. > Cool! Let's look how can my transformer cope with it! > > (spec/form (kw-enum :a :b :c)) > ; (clojure.spec.alpha/and > :my-ns/forgivable-keyword (clojure.core/apply clojure.core/hash-set > enum-values)) > > Ouch! Have I just seen a local symbol in the form? I am sorry third > person, I won't be able to transform the output of your spec-generating > functions. Unless they are macros: > > (defmacro kw-enum [& enum-values] `(spec/and ::forgivable-keyword ~(apply > hash-set enum-values))) > > (spec/form (kw-enum :a :b :c)) > ; (clojure.spec.alpha/and :my-ns/forgivable-keyword #{:c :b :a}) > > This approach looks better. How does it work in combination with other > specs? > > (spec/form (spec/nilable (kw-enum :a :b :c))) > ; (clojure.spec.alpha/nilable (my-ns/kw-enum :a :b :c)) > > There we are. A third-person's function is in the form. We could use eval to > resolve it, but do we want to? > Sure. It resolved to a spec last time, why not this time? OR, why do you need to resolve it at all? (this to some degree depends on the use case) > It has been already evaluated once. What if there are some side effects? > Then I'd say you already have more important problems. > Practically, one has no option to write spec-generating function and > maintain usability of the spec/form at the same time. Therefore one of > four situations must have happened. Either I got something wrong, Specs are > not meant to be introspected, Specs are not meant to be generated or > spec/form is badly designed. Which one is it? > Seems to me like you can generate and introspect this fine. I think that if you are introspecting spec forms, you will encounter resolved symbols referring to functions. How you approach the use of those depends on your context. Another option still coming (work in progress) is to use specs on spec forms (CLJ-2112) to create conformed spec data, then s/unform back to a spec form. > > (In the case of the fourth one I have some suggestions, but lets keep them > for later conversation) > > Thanks for your time > > Marian > -- 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.