Fork me on GitHub
#clojure-spec
<
2017-05-05
>
slipset10:05:30

I seem to remember that the fdef :ret is not enforced/checked under instrument. Is that correct, and if so, how does one go about checking that the return value of a fn conforms to the spec?

slipset13:05:33

The reason I was wondering (and wanting this) is that in cljs, I run my dev-setup with (stest/instrument) which is super nice, but it would be nice to get the :ret bit as well while running in dev-mode

joshjones14:05:34

@slipset The answer you found requires an additional binding in many cases, and will generally mess up the flow of the function. I'm in the same boat as you, looking for a way to automatically check the return, with the ability to disable it for production. Not having the ability to test :ret except through stest makes a function spec much less useful. something like this works at least on a small example:

(defn foo [x y]
  {:pre  [(s/assert (:args (s/get-spec `foo)) [x y])]
   :post [(s/assert (:ret (s/get-spec `foo)) %)]}
  (+ x y))

joshjones14:05:33

it can be turned on/off with s/check-asserts, so it may be a decent workaround to this limitation (it may be by design, but it is still a limitation)

onetom18:05:43

as a https://github.com/Yuppiechef/datomic-schema user, who wrote a few DSLs in Rebol, I was inspired by the data model example in this article: http://blog.cognitect.com/blog/2017/4/6/developing-the-language-of-the-domain since there was no example implementation which could parse this:

(attr :university/full-name string non-blank unique
    "Fully-expanded name of the university for public display")
I set out to implement it with clojure.spec, so I can do:
(->> '[[:person/email one unique str "Email"]
       [:person/org many ref "Orgs"]]
     (mapv (partial s/conform ::attr))
     clojure.pprint/pprint)

onetom18:05:36

here is my implementation:

(defn with-ns [ns kw]
  (keyword ns (name kw)))

(defn conform-with-ns [ns]
  (s/conformer (partial with-ns ns)
               (comp symbol name)))

(defn type-aliases [t]
  ('{str string} t t))

(s/def ::attr
  (s/coll-of
    (s/or :db/doc string?
          :db/cardinality (s/and '#{one many}
                                 (conform-with-ns "db.cardinality"))
          :db/valueType (s/and '#{str string int ref}
                               (s/conformer type-aliases)
                               (conform-with-ns "db.valueType"))
          :db/unique (s/and #{'unique}
                            (conform-with-ns "db.unique"))
          :db/ident keyword?)
    :into {}))

onetom18:05:08

please share your thoughts on it

dnolen18:05:41

I just changed cljs.spec to cljs.spec.alpha now would be a good time to try master - would like to cut a release for this

jimmy02:05:21

dnolen: yay 😄