Основной недостаток заключается в том, что семантика связывания для неопределенных переменных, т. Е. Переменных, не определенных с defvar
и друзьями, изменяется с помощью lexical-binding
: без него let
связывается все динамически, но с lexical-binding
включенными неопределенными переменными связывается лексически , и даже полностью исключается, если не используется в текущей лексической области ,
Старый код иногда опирается на это. Чтобы избежать жестких зависимостей для дополнительных функций, он связывал бы динамические переменные, не требуя соответствующей библиотеки или объявляя саму переменную:
(let ((cook-eggs-enabled t))
(cook-my-meal))
Если функция приготовления является необязательной, мы не хотим навязывать ненужные зависимости пользователю, поэтому мы не используем (require 'cook)
и вместо этого полагаемся на автозагрузку cook-my-meal
функции.
Для читателя-человека очевидно, что cook-eggs-enabled
он не является локальной переменной, но все же ссылается на некоторую глобальную динамическую переменную из cook
библиотеки. Без lexical-binding
этого код работает как задумано: cook-eggs-enabled
связан динамически, независимо от того, определен он или нет.
С lexical-binding
однако, он ломает: cook-eggs-enabled
теперь связан лексически (а затем оптимизируется прочь, потому что она не используется), поэтому глобальная переменная динамического cook-eggs-enabled
будет не когда - либо коснулся вообще , и еще nil
к тому времени , cook-my-meal
называется, так что удивительно , не будет иметь никаких яиц в нашей еде.
К счастью, эти проблемы очень легко обнаружить : байтовый компилятор, естественно, предупреждает о неиспользуемой лексической привязке здесь.
Исправление простое: либо добавьте (require 'cook)
(для функций, которые на самом деле не являются обязательными), либо - чтобы избежать жестких зависимостей - объявите переменную как динамическую переменную в своем собственном коде . Для этого есть специальная defvar
форма:
(defvar cook-eggs-enabled)
Это определяет cook-eggs-enabled
как динамическую переменную, но не влияет на строку документации, load-history
(и, следовательно, на find-variable
друзей) или что-либо еще, кроме характера привязки переменной.
cook-eggs-enabled
станет несвязанным послеlet
завершения? Я почти уверен, что сталкивался с такой ошибкой раньше. Defvar происходил внутриlet
, иlet
позже восстановил переменную в ее начальное (пустое) состояние.