Reputation: 14187
I am trying to build a quick toc from an html content. (to make it short)
The code is dead simple:
(defn toc [content]
(doseq [i (take 5 (iterate inc 1))]
(let [h (str "h" i)]
(println ($ content h)))))
where content is the html content, and $ is a macro required from clojure-soup
While
($ content "h1")
works, and returns a list of all the tags.
Simple:
($ content (str "h" 1))
just won't make it whatever I do.
How do I force
(str "h" 1)
to be properly eval-ed before the macro is called ?
Bonus points for explaining why :)
Upvotes: 4
Views: 142
Reputation: 33637
Amalloy answer the questions why
part. For making it work
part you will need to use eval
.
In place of ($ content h)
use
(eval `($ content ~h))
Another explanation for why this is so is based on the fact that what operations does the macro do at compile time and what it does at runtime (i.e what code it emits). Below is such an example to clear things out.
(def user "ankur")
(defmacro do-at-compile [v] (if (string? v) `true `false))
(defmacro do-at-runtime [v] `(if (string? ~v) true false))
(do-at-compile "hello") ;; => true
(do-at-compile user) ;; => false, because macro does perform the check at compile time
(do-at-runtime "hello") ;; => true
(do-at-runtime user) ;; => true
The $
macro is doing the calculations on the passed second parameter at compile time and hence it is not working in your particular case.
Upvotes: 2
Reputation: 91857
This is not possible if, as you imply, $
is a macro: it's simply not how macros work. The macro needs to expand into something, at compile time, and it can only do so once. You have run-time data, like the various values of h
, but there is no way to use that at compile-time. To me it sounds like $
should have been a function.
Upvotes: 3