Główną pułapką jest to, że semantyka wiązania dla niezdefiniowanych zmiennych - tj. Zmiennych niezdefiniowanych przez defvar
i znajomych - zmienia się z lexical-binding
: Bez tego let
wiąże wszystko dynamicznie, ale z lexical-binding
włączonymi niezdefiniowanymi zmiennymi jest powiązana leksykalnie , a nawet całkowicie pomijana, jeśli nie jest używana w bieżącym zakresie leksykalnym .
Stary kod czasami opiera się na tym. Aby uniknąć twardych zależności dla funkcji opcjonalnych, wiązałoby zmienne dynamiczne bez wymagania odpowiedniej biblioteki lub deklarowania samej zmiennej:
(let ((cook-eggs-enabled t))
(cook-my-meal))
Jeśli funkcja gotowania jest opcjonalna, nie chcemy narzucać użytkownikowi niepotrzebnych zależności, więc nie używamy, (require 'cook)
a zamiast tego polegamy na automatycznym ładowaniu cook-my-meal
funkcji.
Dla ludzkiego czytelnika cook-eggs-enabled
jest oczywiste, że nie jest to zmienna lokalna, ale wciąż odnosi się do jakiejś globalnej zmiennej dynamicznej z cook
biblioteki tutaj. Bez lexical-binding
tego kodu działa zgodnie z przeznaczeniem: cook-eggs-enabled
jest związany dynamicznie, czy określona lub nie.
Dzięki lexical-binding
jednak łamie: cook-eggs-enabled
jest teraz związany leksykalnie (a następnie optymalizowane daleko, ponieważ nie jest używany), więc zmienna globalna dynamiczny cook-eggs-enabled
jest nie kiedykolwiek dotknęła w ogóle i jeszcze nil
przez czas cook-my-meal
nazywa, więc zaskakująco nie będzie miał żadnych jaj w naszym posiłku.
Na szczęście te problemy są bardzo łatwe do wykrycia : kompilator bajtów naturalnie ostrzega przed nieużywanym powiązaniem leksykalnym tutaj.
Poprawka jest prosta: albo dodaj (require 'cook)
(dla funkcji, które tak naprawdę nie są opcjonalne), albo - aby uniknąć twardych zależności - zadeklaruj zmienną jako zmienną dynamiczną we własnym kodzie . Istnieje defvar
do tego specjalny formularz:
(defvar cook-eggs-enabled)
Definiuje to cook-eggs-enabled
jako zmienną dynamiczną, ale nie wpływa na dokumentację, load-history
(a więc find-variable
i znajomych) ani nic innego, z wyjątkiem wiążącej natury zmiennej.
cook-eggs-enabled
być niezwiązany kiedylet
kończy? Jestem prawie pewien, że napotkałem już taki błąd. Defvar działał wewnątrzlet
, alet
później przywrócił zmienną do jej początkowego (pustego) stanu.