Які можливі підводні помилки, що дозволяють лексичним зв'язуванням для буфера?


12

На це надихнуло обговорення лексичного зв’язку проти лексичного в цьому питанні . Оскільки лексичні обов'язковість дає вам можливість мати корисні закриття, до яких люди можуть використовуватись іншими мовами, такими як JavaScript, чому б ви не ввімкнули це постійно?

Якщо припустити сумісність із старішим Emacsen не викликає занепокоєння, які підводні камені ви повинні шукати, якщо включити їх у застарілі буфери коду?

Відповіді:


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як динамічну змінну, але не впливає на docstring, load-history(і, таким чином, find-variableі друзі) або що-небудь інше, крім прив'язної природи змінної.


У динамічному випадку, чи не призведе цей фрагмент коду cook-eggs-enabledдо незв’язаного під час letзавершення? Я майже впевнений, що натрапив на помилку, як це раніше. Дефвара відбувалася всередині let, а letпізніше відновила змінну до її початкового (недійсного) стану.
Малабарба

1
@Malababa Ні, це інша ситуація. Дивіться останній параграф Визначення змінних з причини.
місячник
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.