Arc Forumnew | comments | leaders | submitlogin
Nested quasiquotation question.
1 point by pmarin 5167 days ago | 2 comments
In the expression:

    `(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)

 that evaluates to 

    (a (quasiquote (b (unquote (+ 1 2)) (unquote (foo 4 d)) e)) f)
Why (+ 1 3) is evaluated and (+ 1 2) not?


4 points by aw 5167 days ago | link

The general rule is that you need to unquote as many times as you've quasiquoted to get an immediate evaluation instead of a deferred one.

The why is because it's useful to be able to choose. Suppose I have a macro which itself creates a macro.

  (mac implicit (name)
    `(mac ,(sym (string "w/" name)) (v . body)
       (w/uniq param
         `(let ,param (defvar-impl ,',name)
            ...))))
I use this macro by typing

  (implicit foo)
this expands into

  (mac w/foo (v . body)
    (w/uniq param
      `(let ,param (defvar-impl foo)
         ...)))
notice how I have a choice as to whether I want things evaluated or used as-is in the "w/foo" macro that I'm creating. I only used one comma with "param", and so it was delivered as-is into the generated macro. I used two commas with "name", and so it was evaluated, and its value "foo" was inserted.

If in a two level quasiquote an unquote one level down was immediately evaluated then I'd have no way of inserting the ",param" into the macro I'm creating. And at the time that my outer "implicit" macro runs, "param" doesn't even exist yet.

-----

1 point by rocketnia 5166 days ago | link

You're totally right about the way it works (except for your example; the generated macro actually has (defvar-impl ,'foo) which is effectively the same), but being able to choose isn't the issue, I think.

If quasiquote didn't account for nesting, all we'd have to do is this:

   (mac implicit (name)
     `(mac ,(sym (string "w/" name)) (v . body)
        (w/uniq param
  -       `(let ,param (defvar-impl ,',name)
  +       `(let ,',param (defvar-impl ,name)
             ...))))
To clarify a bit, the ,', idiom doesn't have the same effect in both cases. It essentially breaks out of nested quasiquotes and breaks into non-nested ones. This is thanks to the fact that in the nested case the inner unquote applies first, and in the non-nested case the outer unquote applies first.

We can even simulate this non-nested behavior right now by writing our quasiquotes within ssyntax, so as to hide the inner ones from the quasiquote compiler:

  -       `(let ,param (defvar-impl ,',name)
  +       (quasiquote:let ,',param (defvar-impl ,name)
(And for what it's worth, this change should affect the generated macro by removing the extraneous ,' in (defvar-impl ,'foo).)

~~~

With this in mind, why do we use nested quasiquotes? I'm guessing it's because code is easier to edit this way. We get to wrap a quasiquote template in another template (or to reverse that process) without having to go through and edit every single unquote form. We only edit the unquotes that break out of the wrapping, and we'd have to edit those anyway.

-----