Reputation: 83680
Imagine having a dynamically generated specs for cat
[:first-name string? :surname string?]
Now I want to use them with cljs.spec.alpha/cat
.
In plain Clojure I can write a macro that will cons a macro with args and eval it, right? But for ClojureScript it is not as easy because macros
are compile-time beasts and also eval
is also kinda different thing.
What are workarounds to apply my vector of args to a macro in ClojureScript?
Upvotes: 2
Views: 160
Reputation: 3527
Is your spec generated symbolically at compile time? If so, perhaps you could use a macro as you had suggested.
(ns foo.core)
(defmacro gen-spec []
`'~(into '[:first-name string?] '[:surname string?]))
(defmacro apply-macro [name args-form]
`(~name ~@(eval args-form)))
Here is an example using it
$ clj -m cljs.main
ClojureScript 1.10.339
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (require-macros 'foo.core)
nil
cljs.user=> (foo.core/gen-spec)
[:first-name string? :surname string?]
cljs.user=> (foo.core/apply-macro s/cat (foo.core/gen-spec))
{:cljs.spec.alpha/op :cljs.spec.alpha/pcat, :ps [#object[cljs$core$string_QMARK_] #object[cljs$core$string_QMARK_]], :ret {}, :ks [:first-name :surname], :forms [cljs.core/string? cljs.core/string?], :rep+ nil}
Note how gen-spec
builds up the spec symbolically and returns it in quoted form.
Upvotes: 2
Reputation: 16194
Unfortunately this isn't easy for dynamic/data-driven specs, but that's apparently being worked on for a future release.
I think the only workarounds are to eval
, or use cljs.spec.alpha internal implementation as you mentioned.
cljs.user=> (def cat-args [:x string?])
#'cljs.user/cat-args
cljs.user=> (s/def ::foo (eval (cons 's/cat cat-args)))
:cljs.user/foo
cljs.user=> (s/conform ::foo '("foo"))
{:x "foo"}
Upvotes: 2