Macro argument evaluation, and function return value evaluation?

69 views
Skip to first unread message

Didier

unread,
Mar 12, 2018, 11:06:43 PM3/12/18
to Clojure
I feel these have been asked many times, but no answers I could find was complete, or satisfactory.

1) How do you, within a macro, choose to evaluate an argument? So imagine I wanted a macro which had applicative order of evaluation? Not a macro that returns a function which will be evaluated in applicative order, but actually evaluating the argument inside the macro, to resolve its value, whether it be part of a local, a symbol pointing to a var inside a namespace, or a literal value?

I can only think of:

(defmacro applicative-order
 
[& args]
 
(map eval args))

This works with global vars (def and defn), and it works with literal values, but not with locals.

My intuition tells me there's just no way to use locals inside a macro, since the macro is expanded before the locals are created.

1.1) Is it true that this will always work with globals and literal values?

1.2) Is there a safer way then using eval, which would similarly work with globals as well as literal values?

2) How can I force evaluation of a function's return value?

My intuition was, since I want applicative order of evaluation, to just use a function, but a function will return the code unevaluated.

(defn applicative-order
 
[& args]
 
`(~@args))

This works with globals, locals and literal values, but it returns unevaluated code.

So I thought about:

(defn applicative-order
 
[& args]
 
(eval `(~@args)))

This works, and my intuition tells me its totally fine, apart that I want to be sure not to use it with input from a user/file/io.

2.1) Is there any other way then using eval to do this?

2.2) Is there anything problematic with using eval like this? Any caveats with eval apart from input safety? Like situation where its missing environment data and thus fails to work as expected?

REMARKS:

Now you might wonder why all this is needed, well it turns out if you want to compose a macro, it can be quite tricky, and something like the above helps. Which macro do I want to compose? Well, in my case, s/keys macro from clojure.spec. If I want to create a spec from a map as such:

(def foo {:req [::a ::b]})
(let [bar {:req [::x ::y]}]
 
(s/combine-keys foo bar {:req [::baz]})

Thank You.

Didier

unread,
Mar 13, 2018, 8:33:58 PM3/13/18
to Clojure
Well, I'm still interested in the above questions, but now I realized that spec is even harder to extend, since s/keys doesn't even truly take a standard map of keys and vectors, the vector can have an `or` in it, which is a non standard or, so a default eval does not handle it properly.

Hopefully the next version of spec might make this easier. Maybe I wish spec followed the data > function > macro philosophy, and that took regular clojure datastructures instead of this custom DSL it currently uses.
Reply all
Reply to author
Forward
0 new messages