7

For practice, I've defined

(defmacro quote-paren
  "body -> `(body)"
  [& body]
  `(~@body))

which has the expected transformation (quote-paren body) => ``(body)`. It seems to satisfy a few basic tests:

user=> (macroexpand-1 `(quote-paren 3 4 5))
(3 4 5)
user=> (macroexpand-1 `(quote-paren println "hi"))
(clojure.core/println "hi") 
user=> (macroexpand-1 `(quote-paren (println "hi")))
((clojure.core/println "hi"))

However, I've been testing it with this do-while macro (modified from here):

(defmacro do-while
  [test & body]
  (quote-paren loop [] 
    ~@body
    (when ~test
      (recur))))

(def y 4)
(do-while (> y 0)
  (def y (dec y)))

But the result is

IllegalStateException Attempting to call unbound fn: #'clojure.core/unquote-splicing  clojure.lang.Var$Unbound.throwArity (Var.java:43)

I don't understand this, because from what I can see the `quote-paren' macro works fine (with ~@body plugged in):

user=> (macroexpand-1 
         `(quote-paren loop [] 
            (def y (dec y))
            (when ~test
              (recur))))

(clojure.core/loop [] (def user/y (clojure.core/dec user/y)) (clojure.core/when #<core$test clojure.core$test@1f07f672> (recur)))

But trying to macroexpand do-while causes an "unbound fn". Is there something subtle I'm missing?

Community
  • 1
  • 1
spacingissue
  • 497
  • 2
  • 12

1 Answers1

3

missing the syntax-quote before quote-paren

user> (defmacro do-while
  [test & body]
  `(quote-paren loop [] 
    ~@body
    (when ~test
      (recur))))
#'user/do-while

which then expands properly:

user> (macroexpand '(do-while (> y 0)
                      (def y (dec y))))
(loop* [] (def y (dec y)) (clojure.core/when (> y 0) (recur)))

and seems to work:

user> (def y 4)
#'user/y
user> (do-while (> y 0)
  (def y (dec y)))
nil
user> 
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • 1
    and just as a side comment: using (def y ...) for loop control can have unintended consequences that are unrelated to this question ;-) – Arthur Ulfeldt Oct 09 '12 at 18:53
  • Hmm, you're right, but that sort of defeats the purpose of my exercise. Is there anyway to make a quote-paren that replaces `(quote-paren stuff)` with `'(stuff)`? In short, making it unnecessary to write ` in the macro definition. – spacingissue Oct 09 '12 at 19:39
  • I've tried replacing `(quote-paren stuff)` with `(quote (stuff))` but it seems to stop recognizing the variables; that is `(defmacro do-while [test & body] (quote (loop [] ~@body (when ~test (recur)))))` and running results in `CompilerException java.lang.RuntimeException: Unable to resolve symbol: body in this context, compiling:(NO_SOURCE_PATH:58)` If this is best addressed in another question, though, I'd understand. – spacingissue Oct 09 '12 at 20:07
  • the second part of my answer here: http://stackoverflow.com/questions/12789350/clojure-dynamic-binding talked about writing things like this as an anaphoric macro (a macro that introduces new names) which you could use to get around the need to unquote body (to get it to resolve) – Arthur Ulfeldt Oct 09 '12 at 20:48
  • Is [something like this](http://pastebin.com/vR2PddgN) what you had in mind? (It still doesn't quite work, I think I'll be ready to give up soon.) – spacingissue Oct 09 '12 at 21:38