Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure macro to return two or more s-expressions

Tags:

clojure

Is there a way in Clojure to write a macro (or use an existing one) that returns two or more s-expressions?

Example. Say I am trying to produce the first 10 squares:

(map * (range 10) (range 10))

Now I want to try and use repeat so that I can later provide the power (2 for now) as a parameter:

(map * (repeat 2 (range 10)))

The above however doesn't work as repeat returns a single s-expression with the two ranges, not two ranges.

Please don't provide alternative implementations of a "return the first 10 squares" function. I am interested in idiomatic ways to deal with such situations or generally applicable workarounds.

I suppose I am looking for a way to implement explode below:

(explode '( a b c )) ;; evals to a b c
(map * (explode (repeat 2 (range 10)))
like image 219
Marcus Junius Brutus Avatar asked Apr 03 '13 19:04

Marcus Junius Brutus


2 Answers

As Arthur says, you can't do exactly what you're looking for with macros. Workarounds are possible, depending on what you want to do with those forms.

If you simply want to evaluate the multiple forms produced by the macro, e.g. to simplify boilerplate code, you could wrap those multiple forms in a do form:

(defmacro defn-foos [& foos]
  `(do ~@(map (fn [foo] `(defn ~foo [])) foos)))

(defn-foos my-fn-1 my-fn-2)
;; expands as:
;; (do (defn my-fn-1 [])
;;     (defn my-fn-2 []))

If you want to use the expanded forms as positional arguments in a function call, as it appears you do, then your best bet is probably to wrap them in a list and then use apply which unpacks a list as positional arguments to a function.

To see how this works, note that the second example in your original question can be trivially modified to work using apply:

(apply map * (repeat 2 (range 10)))
like image 42
Alex Avatar answered Nov 05 '22 11:11

Alex


A single macro cannot do this.

A macro is a function that takes an s-expression and transforms it into another s-expression. The inputs can be any sequence of expressions (symbols, primatives, other expressions etc) and the output is a single expression that is inserted in place of the original macro call.

in general the macro would have to surround the call to map and transform the whole form

(your-macro-here (map * (repeat 2 (range 10))))

If you have two macros, one that produces expressions and another that encompasses the entire outer form, the outer macro could perhaps find the single expression containing the macro result, in your example two ranges, and hoist them up one level to be tho independent expressions. I couldn't recommend doing this in practice, it just serves to illustrate the capabilities of macros.

like image 166
Arthur Ulfeldt Avatar answered Nov 05 '22 12:11

Arthur Ulfeldt