Compreendendo símbolos não internos e expansão macro?

8

Quero demonstrar minha falta de conhecimento com um exemplo.

Usando as duas definições de macro a seguir,

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

a primeira macro oculta todas as variáveis ​​nomeadas maxque podem ocorrer no corpo e a segunda não, o que pode ser mostrado com o seguinte exemplo:

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

Tanto quanto eu aprendi, a avaliação de uma chamada macro funciona assim:

A macro é avaliada duas vezes. Primeiro, o corpo é avaliado e retorna um formulário. Este formulário é avaliado novamente.

Por enquanto, tudo bem.

Mas se eu presumir que a macro realmente retorna um pedaço de texto que, por acaso, é uma forma lisp, que depois é interpretada, eu recebo um conflito que me torna incapaz de entender o exemplo acima.

Que parte do texto make-symbolretorna a segunda macro que usa , para que não ocorra sombra? No meu entendimento, um nome de símbolo escolhido aleatoriamente extremamente improvável faria sentido.

Se eu usar as pp-macroexpand...duas macros, retorne a mesma expansão.

Alguém é capaz de me ajudar a sair dessa confusão?

clemera
fonte
11
Não é um pedaço de texto, mas uma lista de símbolos. Alguns dos quais podem ser especiais e, portanto, nunca colidem com outros ...
wasamasa
2
Se você definir print-gensyme print-circlepara tvocê, poderá ver a diferença nas expansões de macro.
npostavs
@wasamasa como eles são especiais e o que faz isso realmente funcionar é exatamente o que eu quero entender.
Clemera
@npostavs Obrigado, então o formulário retornado é diferente e isso não é visível nas configurações padrão ... Vejo que o símbolo não interno é substituído por #:max. O que isso significa ? Estou muito interessado em mais detalhes.
Clemera
3
O #:é apenas uma convenção da impressora para indicar um símbolo uninterned (isto é o que print-gensymse acende), não há mais detalhes no manual: (elisp) Creating Symbols.
NDecls

Respostas:

4

Conforme mencionado nos comentários, é necessário ativar essa configuração para ver como a macroexpansão funciona com mais precisão. Observe que isso muda apenas como macroexpandé exibido, as macros ainda funcionam da mesma maneira em ambos os casos:

(setq print-gensym t)

Em seguida, a primeira instância se expande para (incorreta):

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

E o segundo se expande para (correto):

(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 entender melhor a diferença, considere avaliar isso:

(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 você pode ver, o resultado de (make-symbol "max")não tem valor. Isso garante a correção da primeira passagem de avaliação na macro. Com o segundo passe de avaliação, #:maxganha um valor, pois agora está vinculado como uma variável dinâmica.

abo-abo
fonte
Você também precisa de print-circleoutros 2 #:maxque não são eq(considere também aninhados for).
N
Obrigado, com sua resposta e os comentários, fica muito mais claro para mim. Mas o nome do símbolo permanece no máximo, certo? Então eu supor que há uma maneira o intérprete não sabe para procurar o símbolo uninterned máximo na tabela de símbolos de costume, mas em outra tabela de pesquisa ...
clemera
Na segunda passagem, o símbolo max é uma variável vinculada a let: tudo está bem nesse caso. No primeiro caso, é apenas um símbolo único não interno - não há nenhuma tentativa de interná-lo. Veja cl-gensymse ainda não está claro.
abo-abo
Obrigado pela ajuda. Ainda tenho problemas com os detalhes: na segunda passagem, há o let bound max e o #: max e ainda não entendo como funciona que eles sejam distinguíveis. Se eu chamar o nome do símbolo no make-symbol criado max, também será max. Então, por que o intérprete não está confuso, qual é o mecanismo que ele sabe como procurar o valor certo?
Clemera