¿Comprender los símbolos no internos y la expansión macro?

8

Quiero demostrar mi falta de conocimiento con un ejemplo.

Usando las siguientes dos definiciones de macro,

(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))))))

la primera macro sombrea cualquier variable nombrada maxque pueda ocurrir en el cuerpo y la segunda no, lo que se puede mostrar con el siguiente ejemplo:

(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)))))

Hasta donde sé, la evaluación de una llamada macro funciona así:

La macro se evalúa dos veces. Primero se evalúa el cuerpo y devuelve un formulario. Este formulario luego se evalúa nuevamente.

Hasta aquí todo bien.

Pero si supongo que la macro realmente devuelve un fragmento de texto que resulta ser una forma de ceceo, que luego se interpreta, obtengo un conflicto que me impide comprender el ejemplo anterior.

¿Qué parte del texto make-symboldevuelve la segunda macro que usa , para que no se sombree? En mi opinión, un nombre de símbolo elegido aleatorio extremadamente improbable tendría sentido.

Si uso pp-macroexpand...ambas macros, devuelvo la misma expansión.

¿Alguien puede ayudarme a salir de esta confusión?

clemera
fuente
1
No es un fragmento de texto, sino una lista de símbolos. Algunos de los cuales pueden ser especiales y, por lo tanto, nunca chocan con otros ...
wasamasa
2
Si se establece print-gensymy print-circleque tusted será capaz de ver la diferencia en las expansiones de macro.
npostavs
@wasamasa cómo son especiales y qué hace que esto realmente funcione es exactamente lo que quiero entender.
clemera
@npostavs Gracias, por lo que el formulario devuelto es diferente y esto simplemente no es visible con la configuración predeterminada ... Veo que el símbolo no interno se reemplaza por #:max. Qué significa eso ? Estoy muy interesado en más detalles.
clemera
3
El #:es simplemente una convención de la impresora para indicar un símbolo uninterned (esto es lo que print-gensymse enciende), hay más detalle en el manual: (elisp) Creating Symbols.
npostavs

Respuestas:

4

Como se menciona en los comentarios, debe activar esta configuración para ver cómo funciona la macroexpansión con mayor precisión. Tenga en cuenta que solo cambia la forma en que macroexpandse muestra, las macros siguen funcionando igual en cualquier caso:

(setq print-gensym t)

Luego, la primera instancia se expande a (incorrecta):

(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)))))

Y el segundo se expande a (correcto):

(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)))))

Para comprender mejor la diferencia, considere evaluar esto:

(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

Como puede ver, el resultado de (make-symbol "max")no tiene valor. Esto garantiza la exactitud del primer paso de evaluación en la macro. Con el segundo paso eval, #:maxgana un valor ya que ahora está vinculado como una variable dinámica.

abo-abo
fuente
También necesita, de lo print-circlecontrario, tiene 2 #:maxque no están eq(considere también anidados for).
npostavs
Gracias, con su respuesta y los comentarios se me hace mucho más claro. Pero el nombre del símbolo sigue siendo el máximo ¿verdad? Así que supongo que hay una forma en que el intérprete sabe que no debe buscar el símbolo uninterned máximo en la tabla de símbolos de costumbre, pero en otra tabla de búsqueda ...
clemera
En la segunda pasada, el símbolo max es una variable limitada: todo está bien en ese caso. En el primer caso, es solo un símbolo único no intercalado: no hay ningún intento de internarlo. Echa un vistazo cl-gensymsi aún no está claro.
abo-abo
Gracias por tu ayuda. Todavía tengo problemas con los detalles: en la segunda pasada está el límite máximo let y el #: max y todavía no entiendo cómo funciona que sean distinguibles. Si llamo nombre-símbolo en el símbolo de creación creado max, también es max. Entonces, ¿por qué el intérprete no se confunde? ¿Cuál es el mecanismo que sabe cómo buscar el valor correcto?
clemera