Evaluating functions inside macro's body

Hello guys. I’m learning web apps with Clojure from scratch and I wanted to make a macro that would hold a common template using Hiccup for my pages, like this:

(defmacro defpage
  [elements]
  (html
   (include-css "https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css")
   elements))

I’m calling my macro with some functions inside the structure representing the HTML. That’s a dumb example for sake of simplicity:

(defpage [:p (+ 1 2)])

Well, I know that macro content is not evaluated by default, so the result is this:

<link href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css\" rel=\"stylesheet\" type=\"text/css\" /><p>+12</p>

See the “+12”. Would be better if it was a “3”. If I tell my macro to (eval elements) this works as I expect, but I wonder if this does not kinda “breaks” the purpose of macros.

What are your thoughts?

Well, I tested a macro that is not using Hiccup’s html function and it worked as I expected without any modifications. That might be something that happens inside html function that causes this behavior.

Why are you making it a macro to begin with? Just make it a function and it should work as expected.

Just for sake of practice. I know that a function would do the job.

Well, you can’t practice learning about macros without a use case that requires one.

That’s the problem you are facing.

Yes it would defeat the purpose of macros, as the purpose of macros is to NOT perform evaluation of the arguments, and in your case elements.

So if the first thing you did in your macro was to evaluate your arguments in order of left to right, and then only used the evaluated results for the rest of your logic, you basically made a macro behave as a function.

Did you mean like this?:

(defmacro defpage [elements]
  elements)

(defpage [:p (+ 1 2)])
;; => [:p 3]

Okay, let’s see how I can best explain this.

First try the above, and then try this:

(defmacro defpage [elements]
  (println elements))

(defpage [:p (+ 1 2)])
;; [:p (+ 1 2)]
;; => nil

A macro receives its arguments as if they were quoted.

So elements is going to be equal too: '[:p (+ 1 2)].

If you called (println '[:p (+ 1 2)] what would you expect to be printed? It’s the same for the macro.

Whatever a macro returned is further evaluated. So in the case of the macro returning elements, the macro is returning '[:p (+ 1 2)]. And this will be further evaluated automatically, because what macros return are in turn evaluated, so that’s why you end up seeing [:p 3].

Now back to your real defpage. Can you guess what is happening?

Elements is quoted, meaning it is a vector with first element a keyword and second element a list. What does Hiccup do when it receives a list?

And you can try it by just running:

(html
   (include-css "https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css")
   '[:p (+ 1 2)])
1 Like

Ok, now I think I understand what’s happening. Passing a list to Hiccup is equivalent to unquote splicing it, right? Thanks for the explanation.

Ya, pretty much. The link I posted goes to the doc for it. Don’t know why clojureverse shows it like that though annoying.

[:p (list + 1 2)] is the same as [:p + 1 2]

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.