Eine große Gefahr besteht darin, dass sich die Bindungssemantik für undefinierte Variablen - dh Variablen, die nicht mit defvar
und friends definiert sind - ändert lexical-binding
: Ohne sie wird let
alles dynamisch lexical-binding
gebunden , 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-enabled
ist es offensichtlich, dass es sich nicht um eine lokale Variable handelt, sondern dass hier auf eine globale dynamische Variable aus der cook
Bibliothek verwiesen wird. Ohne lexical-binding
diesen Code funktioniert wie beabsichtigt: cook-eggs-enabled
Wird dynamisch gebunden, ob definiert oder nicht.
Mit lexical-binding
aber bricht es: cook-eggs-enabled
jetzt gebunden ist lexikalisch (und dann weg optimiert, weil es nicht verwendet wird ), so dass die globale dynamische Variable cook-eggs-enabled
ist nicht immer überhaupt und noch berührt nil
von der Zeit cook-my-meal
aufgerufen 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 defvar
Formular:
(defvar cook-eggs-enabled)
Dies wird cook-eggs-enabled
als dynamische Variable definiert , hat jedoch keine Auswirkungen auf die Dokumentzeichenfolge, die load-history
(und damit auch auf find-variable
Freunde) oder andere Elemente, mit Ausnahme der Bindungsart der Variablen.
cook-eggs-enabled
seinen ungebundene wennlet
beendet? Ich bin mir ziemlich sicher, dass ich schon einmal auf einen solchen Bug gestoßen bin. Die Defvar ereignete sich in derlet
, und dielet
Variable wurde später wieder in den ursprünglichen (ungültigen) Zustand versetzt.