elisp – ¿Por qué el alcance de defvar funciona de manera diferente sin un valor de inicio?

Pregunta:

Supongamos que tengo un archivo llamado elisp-defvar-test.el contiene:

;;; elisp-defvar-test.el ---  -*- lexical-binding: t -*- 

(defvar my-dynamic-var)

(defun f1 (x)
  "Should return X."
  (let ((my-dynamic-var x))
    (f2)))

(defun f2 ()
  "Returns the current value of `my-dynamic-var'."
  my-dynamic-var)

(provide 'elisp-dynamic-test)

;;; elisp-defvar-test.el ends here

Cargué este archivo y luego entro en el búfer temporal y ejecuto:

(setq lexical-binding t)
(f1 5)
(let ((my-dynamic-var 5))
  (f2))

(f1 5) devuelve 5 como se esperaba, lo que indica que el cuerpo de f1 está tratando my-dynamic-var como una variable de ámbito dinámico, como se esperaba. Sin embargo, la última forma da un error de variable nula para my-dynamic-var , lo que indica que está usando un alcance léxico para esta variable. Esto parece estar en desacuerdo con la documentación de defvar , que dice:

La forma defvar también declara la variable como "especial", de modo que siempre está vinculada dinámicamente incluso si lexical-binding es t.

Si cambio la forma defvar en el archivo de prueba para proporcionar un valor inicial, entonces la variable siempre se trata como dinámica, como dice la documentación. ¿Alguien puede explicar por qué el alcance de una variable está determinado por si defvar recibió o no un valor inicial al declarar esa variable?

Aquí está el seguimiento del error, en caso de que importe:

Debugger entered--Lisp error: (void-variable my-dynamic-var)
  f2()
  (let ((my-dynamic-var 5)) (f2))
  (progn (let ((my-dynamic-var 5)) (f2)))
  eval((progn (let ((my-dynamic-var 5)) (f2))) t)
  elisp--eval-last-sexp(t)
  eval-last-sexp(t)
  eval-print-last-sexp(nil)
  funcall-interactively(eval-print-last-sexp nil)
  call-interactively(eval-print-last-sexp nil nil)
  command-execute(eval-print-last-sexp)

Respuesta:

La razón por la que los dos son tratados de manera diferente es principalmente "porque eso es lo que necesitábamos". Más específicamente, la forma de argumento único de defvar apareció hace mucho tiempo, pero más tarde que la otra y fue básicamente un "truco" para silenciar las advertencias del compilador: en el momento de la ejecución no tuvo ningún efecto, por lo que, como un "accidente", significaba que el comportamiento de silenciamiento de (defvar FOO) solo se aplicaba al archivo actual (ya que el compilador no tenía forma de saber que tal defvar se había ejecutado en algún otro archivo).

Cuando lexical-binding se introdujo en Emacs-24, decidimos volver a utilizar este (defvar FOO) forma, sino que implica que ahora tiene un efecto.

En parte para preservar el comportamiento anterior de "solo afecta el archivo actual", pero lo más importante es permitir que una biblioteca use toto como una var de ámbito dinámico sin evitar que otras bibliotecas usen toto como una var de ámbito léxico (generalmente la convención de nomenclatura de prefijo de paquete evita esos conflictos, pero lamentablemente no se usa en todas partes), el nuevo comportamiento de (defvar FOO) se definió para aplicarse solo al archivo actual, e incluso se refinó para que solo se aplique al alcance actual (por ejemplo, si aparece dentro de una función , solo afecta los usos de esa var dentro de esa función).

Fundamentalmente, (defvar FOO VAL) y (defvar FOO) son solo dos cosas "completamente diferentes". Simplemente usan la misma palabra clave por razones históricas.

Leave a Comment

Your email address will not be published.

Scroll to Top

istanbul avukat

-

web tasarım