Was sind die potenziellen Gefahren für die Aktivierung der lexikalischen Bindung eines Puffers?

12

Dies wurde durch die Diskussion von lexikalischer Bindung im Vergleich zu lexikalischem Let in dieser Frage angeregt . Da Sie mithilfe der lexikalischen Bindung die Möglichkeit haben, nützliche Abschlüsse zu haben, sind die Benutzer möglicherweise an andere Sprachen wie JavaScript gewöhnt. Warum haben Sie sie nicht die ganze Zeit aktiviert?

Angenommen, die Abwärtskompatibilität mit älteren Emacsen-Versionen ist kein Problem. Nach welchen Fallstricken sollten Sie suchen, wenn Sie sie in älteren Codepuffern aktivieren?

Stsquad
quelle

Antworten:

13

Eine große Gefahr besteht darin, dass sich die Bindungssemantik für undefinierte Variablen - dh Variablen, die nicht mit defvarund friends definiert sind - ändert lexical-binding: Ohne sie wird letalles dynamisch lexical-bindinggebunden , aber mit aktivierten undefinierten Variablen wird es lexikalisch gebunden und sogar vollständig beseitigt, wenn es im aktuellen lexikalischen Bereich nicht verwendet wird .

Alter Code stützt sich manchmal darauf. Um harte Abhängigkeiten für optionale Features zu vermeiden, würde es dynamische Variablen binden, ohne die entsprechende Bibliothek zu benötigen oder die Variable selbst zu deklarieren:

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

Wenn die Kochfunktion optional ist, möchten wir dem Benutzer keine unnötigen Abhängigkeiten (require 'cook)aufzwingen cook-my-meal. Daher verwenden wir die Funktion nicht und verlassen uns stattdessen auf das automatische Laden der Funktion.

Für den menschlichen Leser cook-eggs-enabledist es offensichtlich, dass es sich nicht um eine lokale Variable handelt, sondern dass hier auf eine globale dynamische Variable aus der cookBibliothek verwiesen wird. Ohne lexical-bindingdiesen Code funktioniert wie beabsichtigt: cook-eggs-enabledWird dynamisch gebunden, ob definiert oder nicht.

Mit lexical-bindingaber bricht es: cook-eggs-enabledjetzt gebunden ist lexikalisch (und dann weg optimiert, weil es nicht verwendet wird ), so dass die globale dynamische Variable cook-eggs-enabledist nicht immer überhaupt und noch berührt nilvon der Zeit cook-my-mealaufgerufen wird , so werden wir überraschenderweise keine Eier in unserer Mahlzeit.

Glücklicherweise sind diese Probleme sehr leicht zu erkennen : Der Byte-Compiler warnt hier natürlich vor einer nicht verwendeten lexikalischen Bindung.

Die Lösung ist einfach: Fügen Sie entweder eine hinzu (require 'cook)(für Funktionen, die ohnehin nicht wirklich optional sind), oder deklarieren Sie die Variable als dynamische Variable in Ihrem eigenen Code , um harte Abhängigkeiten zu vermeiden . Hierfür gibt es ein spezielles defvarFormular:

(defvar cook-eggs-enabled)

Dies wird cook-eggs-enabledals dynamische Variable definiert , hat jedoch keine Auswirkungen auf die Dokumentzeichenfolge, die load-history(und damit auch auf find-variableFreunde) oder andere Elemente, mit Ausnahme der Bindungsart der Variablen.

Mondhorn
quelle
Im dynamischen Fall würde nicht , dass Code - Schnipsel Ursache cook-eggs-enabledseinen ungebundene wenn letbeendet? Ich bin mir ziemlich sicher, dass ich schon einmal auf einen solchen Bug gestoßen bin. Die Defvar ereignete sich in der let, und die letVariable wurde später wieder in den ursprünglichen (ungültigen) Zustand versetzt.
Malabarba
1
@ Malababa Nein, das ist eine andere Situation. Den Grund finden Sie im letzten Absatz von Definieren von Variablen .
Lunaryorn