Каковы потенциальные ловушки включения лексического связывания для буфера?

12

Это было вдохновлено обсуждением лексической привязки против лексической-let в этом вопросе . В лексико-связывающим дает возможность иметь полезные затворы людей могут быть использованы для других языков , таких как JavaScript , почему бы вы не позволили ему все время?

Предполагая обратную совместимость со старым Emacsen, это не проблема, какие подводные камни следует искать, если включить его в устаревших буферах кода?

stsquad
источник

Ответы:

13

Основной недостаток заключается в том, что семантика связывания для неопределенных переменных, т. Е. Переменных, не определенных с 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друзей) или что-либо еще, кроме характера привязки переменной.

lunaryorn
источник
В динамическом случае этот фрагмент кода не cook-eggs-enabledстанет несвязанным после letзавершения? Я почти уверен, что сталкивался с такой ошибкой раньше. Defvar происходил внутри let, и letпозже восстановил переменную в ее начальное (пустое) состояние.
Малабарба
1
@ Малабаба Нет, это другая ситуация. См. Последний абзац определения переменных по причине.
lunaryorn