Fork me on GitHub
#clojure-spec
<
2019-12-03
>
Quest09:12:30

Anyone have a convenient solution for defining clojure specs at runtime? Channel logs say either wrap with another macro OR to use eval. Macro approach requires everything else to become a macro & still doesn't allow full runtime extension. Eval works but creates more complexity to get it working in CLJS.

Quest09:12:44

Interestingly, I can simply copy my own version of res (private FN from clojure.spec.alpha) & suddenly the below works.

(s/def-impl k (res v) v)
While hackish it proves that dynamic definition works just fine, but seems to have been intentionally disallowed? Though I still prefer over the eval nonsense. (Which for completeness sake is included below:)
(eval (list 's/def k v))

Quest09:12:20

Looks like CLJS still hiccups on the def-impl approach due this a difference in how clojure.core/resolve works. It's a macro in CLJS but a FN in Clojure? (I'm curious how the impl differs under the hood if anyone knows...) The offending line:

(symbol? form) (c/or (-> form resolve ->sym) form)
In my case I'm just passing namespaced keywords at runtime, so this part is unnecessary for me. Simply commenting it out obviates the issue. I'm sure this breaks many best practice recommendations, but if this is the most direct solution then I'm happy with it. I understand that spec2 should support this more easily & development is focused there, meaning that spec1 is unlikely to change & break this approach.

Alex Miller (Clojure team)10:12:58

Correct. There are multiple non-macro ways to make this work now in spec 2

👍 4