Подтвердить что ты не робот

Избавиться от предупреждений байт-компиляции "ссылка на свободную переменную"

Я пишу основной режим emacs, который использует буфер-локальные переменные для хранения некоторого состояния:

(defun foo-mode ()
  "My nice major mode"
  (interactive)
  (kill-all-local-variables)
  (setq mode-name "foo")
  (setq major-mode 'foo-mode)
  (set (make-local-variable 'foo-state) "bar"))

(defun foo-change-state ()
  (setq foo-state "baz"))

Это работает очень хорошо и обладает тем свойством, что в любом буфере, не использующем мой основной режим, переменная foo-state не привязана (что, на мой взгляд, очень хорошо, поскольку она позволяет избежать загромождения таблицы символов).

Однако, байт-компиляция такой части кода вызывает следующее предупреждение:

Warning: assignment to free variable `foo-state'

Использование defvar избавляет от предупреждения, но имеет побочный эффект, который foo-state теперь привязан повсюду, что нежелательно, на мой взгляд.

Есть ли способ избавиться от предупреждений, пока не привязывает переменные, зависящие от режима, в каждом буфере? Или я ошибаюсь, когда думаю, что эти переменные не должны объявляться глобально?

4b9b3361

Ответ 1

Официальный способ сделать то, что вам нужно, - (defvar foo-state). Обратите внимание на отсутствие второго аргумента. Также обратите внимание, что такое объявление применяется только к файлу, в котором он найден (или к области, в которой он найден, если он используется внутри функции).

Ответ 2

Объявите переменную с помощью defvar. Другого способа удалить предупреждение нет, и это действительно считается хорошей практикой.

Ваше намерение сохранить таблицу символов незанятой, достойно, но вы на самом деле этого не делаете. Я думаю, вы неправильно поняли семантику привязок переменных в Emacs Lisp, так как вы, кажется, полагаете, что, не объявив, что foo-state будет несвязано в любом буфере, не используя foo-mode. Это не так.

В Emacs Lisp имена (aka символы) являются глобальными. Как только foo-state оценивается в первый раз, среда выполнения создает новый объект символа для foo-state и помещает это в глобальную таблицу символов (aka obarray). Нет таблиц локальных символов, поэтому не имеет значения, где оценивается foo-state и как, foo-state относится к одному и тому же символьному объекту в любом месте (см. Создание символов).

Каждый объект символа состоит из компонентов (aka cells), один из которых является переменной ячейкой (см. Компоненты символов). setq изменяет текущую привязку системы, на верхнем уровне без лексической привязки, это эффективно изменяет ячейку переменной объекта символа, таким образом, глобальное значение переменной. Опять же, не имеет значения, где setq оценивается. На самом деле, если некоторый bar-mode оцененный (setq foo-state "bar"), foo-state тоже будет привязан к "bar" в foo-mode и наоборот.

Таким образом, единственный эффект (defvar) над (setq), который документирует намерение использовать символ как глобальную переменную, поэтому рассказывать другим о том, чтобы не изменять эту переменную, если не предназначено поведение поведения foo-mode. Вы можете прикрепить документацию к переменной и пометить ее как определенную в своем буфере (C-h v foo-state предоставит ссылку для перехода к определению).

Поскольку Emacs Lisp не имеет пространств имен и по умолчанию - с динамическим охватом, документация принципиально важна, чтобы избежать конфликтов между модулями. Если бы я написал bar-mode с помощью foo-mode, я мог бы случайно привязаться к foo-state, вызовите foo-change-state, а затем просмотрите мой режим с ошибкой, потому что переменная была непреднамеренно перезаписана. Объявление foo-state не делает это невозможным, но по крайней мере позволяет мне поймать ошибку, потому что C-h v foo-state покажет, что эта переменная используется другим режимом, поэтому лучше не использовать ее, если я действительно не намерен манипулировать этим режимом.

В качестве последнего слова: во всем вышеупомянутом текстовом "режиме" можно заменить файлы Emacs Lisp. modes ничего особенного в отношении символов. Все вышеперечисленное также относится к Emacs Lisp, которые не объявляют режимы, а просто содержат кучу функций.