6

I am trying to implement a macro to recursively converting an infix list into a prefix one. I encounter a problem as follows:

;;this works
(defmacro recursive-infix [form]
  (list (second form) (first form)
        (if (not (seq? (nth form 2)))
          (nth form 2)
          (recursive-infix (nth form 2)))))

;;this doesn't work
(defmacro my-recursive-infix [form]
  `(~(second form) ~(first form)
        (if (not (seq? ~(nth form 2)))
          ~(nth form 2)
          (my-recursive-infix ~(nth form 2)))))

(macroexpand '(recursive-infix (10 + 10))) 
;;get (+ 10 10)

(macroexpand '(my-recursive-infix (10 + 10))) 
;;get (+ 10 (if (clojure.core/not (clojure.core/seq? 10)) 10 (user/my-recursive-infix 10)))

(recursive-infix (10 + 10))
;;get 20

(my-recursive-infix (10 + 10))
;;Don't know how to create ISeq from: java.lang.Integer [Thrown class java.lang.IllegalArgumentException]

Where is the problem? How to correctly define a macro with code templating?

P.S. I changed the code into this and it works, why? what is the difference?:

(defmacro my-recursive-infix [form]
  (if (not (seq? (nth form 2)))
    `(~(second form) ~(first form) ~(nth form 2))
    `(~(second form) ~(first form) (my-recursive-infix (nth form 2)))))
1
  • Does it have anything to do with putting "if block" into the binding range of backquote? Commented Dec 24, 2011 at 4:47

1 Answer 1

13

In the original version, the (if (not ...)) check was happening at compile-time; you've instead included it in the expanded code. So here's a minimal change that would get it to act like you want - it's effectively the same as the original, but "flipping" what's quoted and what's not.

 (defmacro my-recursive-infix [form]
  (let [third (nth form 2)]
    `(~(second form) ~(first form)
      ~(if (not (seq? third))
         third
         `(my-recursive-infix ~third)))))

However, it's a bit nicer to use destructuring to pull out the pieces of the form ahead of time, rather than in-place:

(defmacro my-recursive-infix [form]
  (let [[x op y] form]
    `(~op ~x ~(if (not (seq? y))
                y
                `(my-recursive-infix ~y)))))

And better still, really, is to move the non-recursive case outside, so that (a) it works for literal numbers, and (b) the code looks more like what it expands to:

(defmacro my-recursive-infix [form]
  (if-not (seq? form)
    form
    (let [[x op y] form]
      `(~op ~x (my-recursive-infix ~y)))))
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks very much @amalloy - this really gives me a less convoluted way to attack my problem (stackoverflow.com/questions/41555991/…), but I'm trying to understand the exact steps in the last code block. Is recursion working correctly in this case? I'm passing (1 + 2 + 3) in as form, but it's only returning the first two operands' sum. Also, am I correct in thinking the seq? is to check the recurred y value is a valid form to determine whether or not to recur again?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.