¿Cuáles son los posibles peligros de habilitar el enlace léxico para un búfer?

12

Esto se inspiró en la discusión de ligadura léxica vs lexical-let en esta pregunta . Como el enlace léxico le brinda la posibilidad de tener cierres útiles a los que la gente puede estar acostumbrada en otros lenguajes como JavaScript, ¿por qué no lo habilita todo el tiempo?

Suponiendo que la compatibilidad con versiones anteriores con Emacsen anterior no es una preocupación, ¿qué trampas debe buscar si lo habilita en los búferes de código heredados?

stsquad
fuente

Respuestas:

13

Un obstáculo importante es que la semántica de enlace para variables indefinidas , es decir, variables no definidas con defvary amigos, cambia con lexical-binding: Sin ella, lo letune todo dinámicamente, pero con las lexical-bindingvariables indefinidas habilitadas están unidas léxicamente , e incluso se eluyen completamente si no se usan en el ámbito léxico actual. .

El código antiguo a veces se basa en esto. Para evitar dependencias difíciles para las características opcionales, vincularía variables dinámicas sin requerir la biblioteca correspondiente o declarar la variable en sí:

(let ((cook-eggs-enabled t))
  (cook-my-meal))

Si la función de cocción es opcional, no queremos forzar dependencias innecesarias en el usuario, por lo que no utilizamos (require 'cook)y en su lugar confiamos en la carga automática de la cook-my-mealfunción.

Es obvio para el lector humano que cook-eggs-enabledno es una variable local, pero aún se refiere a alguna variable dinámica global de la cookbiblioteca aquí. Sin lexical-bindingeste código funciona según lo previsto: cook-eggs-enabledestá vinculado dinámicamente, ya sea definido o no.

Con lexical-bindingobstante, se rompe: cook-eggs-enabledahora está obligado léxico (y luego optimizado de distancia, ya que no se usa), por lo que la variable dinámica mundial cook-eggs-enabledestá no siempre toca en absoluto y todavía nilen el momento en cook-my-mealque se llama, por lo que es sorprendente que no vamos a tener ningún huevo en nuestra comida

Afortunadamente, estos problemas son muy fáciles de detectar : el compilador de bytes advierte naturalmente sobre un enlace léxico no utilizado aquí.

La solución es simple: agregue un (require 'cook)(para características que no son realmente opcionales de todos modos) o, para evitar dependencias difíciles, declare la variable como variable dinámica en su propio código . Hay una defvarforma especial para esto:

(defvar cook-eggs-enabled)

Esto se define cook-eggs-enabledcomo variable dinámica, pero no afecta la cadena de documentación, load-history(y por lo tanto find-variabley amigos) ni nada más, excepto la naturaleza de enlace de la variable.

Lunaryorn
fuente
En el caso dinámico, ¿ese fragmento de código nocook-eggs-enabled se desvincularía cuando letfinalice? Estoy bastante seguro de que me he encontrado con un error como este antes. El defvar estaba ocurriendo dentro del let, y el letposterior restableció la variable a su estado inicial (nulo).
Malabarba
1
@Malababa No, esa es una situación diferente. Consulte el último párrafo de Definición de variables para conocer el motivo.
lunaryorn