2

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?

3
  • I think I can't do it, so I'll go using cat-impl directly. Commented Aug 31, 2018 at 12:33
  • Is the vector defined at compile-time or will it be dynamically constructed at runtime? Commented Aug 31, 2018 at 16:49
  • @olieidel it will be defined at compile time (see github.com/fl00r/trout) Commented Aug 31, 2018 at 17:03

2 Answers 2

2

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"} 
Sign up to request clarification or add additional context in comments.

4 Comments

Oh, sorry, I am talking about cljs env, not clojure one. you don't have args on compile time, and macros are not runtime in cljs
are you using cljs.core/eval? because it shouldn't work as you wrote unless you redefined *eval* (cljs.github.io/api/cljs.core/STARevalSTAR)
@fl00r yes, I just tried (cljs.core/eval (cons 's/cat cat-args)) and it works. FWIW I'm doing this in a Planck REPL.
seems like a decent tool
2

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.

1 Comment

Thank you Mike for your feedback. Unfortunately the specs themselves are being generated from another data structure in compile time. So I don't have them in a ready to use form. Look an example in Readme (github.com/fl00r/trout). So I can't use your approach.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.