Set, let, macros, nuts

I am trying to create a fast toc from html content. (to make it short)

The code is dead simply:

(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 the macro required by clojure -soup

Until

 ($ content "h1") 

works and returns a list of all tags.

Plain:

 ($ content (str "h" 1)) 

just won't do it, no matter what I do.

How to make

 (str "h" 1) 

to be correctly evaluated before calling the macro?

Bonus points for explaining why :)

+4
source share
2 answers

This is not possible if, as you imply, $ is a macro: the macro just doesn't work. A macro needs to expand into something at compile time, and it can only do this once. You have runtime data, such as various h values, but there is no way to use them at compile time. It seems to me that $ should have been a function.

+3
source

Amalomna will answer questions why . For the part of making it work you will need to use eval .

Instead of ($ content h) use

 (eval `($ content ~h)) 

Another explanation of why this is so is based on what operations the macro performs at compile time and what it does at run time (that is, what code it emits). The following is an example to clarify the situation.

 (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 performs calculations on the second parameter passed at compile time and therefore does not work in your particular case.

+2
source

Source: https://habr.com/ru/post/1440910/


All Articles