7

I want to demonstrate my lack of knowledge with an example.

Using the following two macro defintions,

(defmacro for (var from init to final do &rest body)
  "Execute a simple for loop: (for i from 1 to 10 do (print i))."
  (let ((tempvar 'max))
    `(let ((,var ,init)
           (,tempvar ,final))
       (while (<= ,var ,tempvar)
         ,@body
         (setq ,var (1+ ,var))))))
(defmacro for (var from init to final do &rest body)
  "Execute a simple for loop: (for i from 1 to 10 do (print i))."
  (let ((tempvar (make-symbol "max")))
    `(let ((,var ,init)
           (,tempvar ,final))
       (while (<= ,var ,tempvar)
         ,@body
         (setq ,var (1+ ,var))))))

the first macro shadows any variables named max which may occur in the body and the second does not, which can be showed with the following example:

(let ((max 0)) ;; this variable is shadowed if the first macro defintion is used
  (for i from 1 to 3 do
       (setq square (* i i))
       (if (> square max) 
         (princ (format "\n%d %d" i square)))))

As far as I have learned the evaluation of a macro call works like this:

The macro gets evaluated twice. First the body gets evaluated and returns a form. This form then gets evaluated again.

So far so good.

But if I assume that the macro really returns a chunk of text which happens to be a lisp form, which then gets interpreted, I get a conflict that makes me unable to understand the example above.

What chunk of text does the second macro which uses make-symbol return, so that no shadowing occurs? In my understanding an extreme unlikely random choosen symbol name would make sense.

If I use pp-macroexpand... both macros return the same expansion.

Is someone able to help me out of this confusion?

Drew
  • 75,699
  • 9
  • 109
  • 225
clemera
  • 3,401
  • 13
  • 40
  • 1
    It's not a chunk of text, but a list of symbols. Some of which can be special and therefore never collide with others... – wasamasa Dec 16 '15 at 17:49
  • 2
    If you set `print-gensym` and `print-circle` to `t` you will be able to see the difference in the macro expansions. – npostavs Dec 16 '15 at 17:50
  • @wasamasa how they are special and what make this actually work is exactly what I want to understand. – clemera Dec 16 '15 at 19:13
  • @npostavs Thanks, so the returned form is different and this is just not visible with the default settings.... I see the uninterned symbol gets replaced by `#:max`. What does that mean ? I'm highly interested in more details. – clemera Dec 16 '15 at 19:16
  • The uninterned symbol doesn't get replaced at all. – npostavs Dec 16 '15 at 19:25
  • @npostavs Ok, what I meant is that the variable gets replaced with `#:max`. I'm not an expert, sometimes it's hard to get all the terminology right. I don't know what uninterned symbol really means. I suppose that is the real reason why I don't understand this. Hopefully someone can explain this in more detail. – clemera Dec 16 '15 at 20:04
  • 3
    The `#:` is just a convention of the printer to indicate an uninterned symbol (this is what `print-gensym` turns on), there is more detail in the manual: [`(elisp) Creating Symbols`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Symbols.html). – npostavs Dec 16 '15 at 22:34

1 Answers1

5

As mentioned in the comments, you have to turn on this setting to see how macro-expansion works more precisely. Note that it only changes how macroexpand is displayed, the macros still work the same in either case:

(setq print-gensym t)

Then the first instance expands to (incorrect):

(let ((max 0))
  (let ((i 1)
        (max 3))
    (while (<= i max)
      (setq square (* i i))
      (if (> square max)
          (princ
           (format "\n%d %d" i square)))
      (setq i (1+ i)))))

And the second one expands to (correct):

(let ((max 0))
  (let
      ((i 1)
       (#:max 3))
    (while
        (<= i #:max)
      (setq square
            (* i i))
      (if
          (> square max)
          (princ
           (format "\n%d %d" i square)))
      (setq i
            (1+ i)))))

To further understand the difference, consider evaluating this:

(setq max 10)
;; => 10
(symbol-value 'max)
;; => 10
(symbol-value (make-symbol "max"))
;; => Caught unbound variable #:max, setting it to nil.
(make-symbol "max")
;; => #:max
(eq (make-symbol "max")
    (make-symbol "max"))
;; => nil

As you can see, the result of (make-symbol "max") has no value. This ensures the correctness of the first eval pass on the macro. With the second eval pass, #:max gains a value since it's now bound as a dynamic variable.

abo-abo
  • 13,943
  • 1
  • 29
  • 43
  • You need also `print-circle` otherwise you have 2 `#:max` that are not `eq` (consider also nested `for`). – npostavs Dec 17 '15 at 14:25
  • Thanks, with your answer and the comments it becomes much clearer to me. But the symbol name remains max right? So I assume there is a way the Interpreter knows not to lookup the uninterned symbol max in the usual symbol table but in another lookup table... – clemera Dec 18 '15 at 09:59
  • On the second pass, the symbol max is a let-bound variable: all is fine in that case. In the first case, it's just a unique uninterned symbol - there's no attempt to intern it. Have a look at `cl-gensym` if it's still not clear. – abo-abo Dec 18 '15 at 11:07
  • Thanks for your help. I still have problems with the details: On the second pass there is the let bound max and the #:max and I still don't understand how it works that they are distinguishable. If I call symbol-name on the make-symbol created max it is max, too. So why is the interpreter not confused, what is the mechanism that he knows how to lookup the right value? – clemera Dec 19 '15 at 10:01