Что делает `setq-local` и когда мне его использовать?

16

Мне не совсем понятны все варианты локальных переменных буфера, даже после прочтения всех документов и множества публикаций здесь на SX.

Вот краткое изложение моего понимания:

(defvar foo ..)объявляет динамическую переменную для файла. Но переменная (1) не известна другим файлам, если они не содержат также и defvar оператор, и (2) переменная является глобальной по объему, а не локальной буферизацией.

(make-variable-buffer-local foo)после defvarвышесказанного сообщаем компилятору и всем остальным, что переменная foo должна рассматриваться как локальная по отношению к буферу везде, где она установлена, когда она установлена. Таким образом, этот шаблон является хорошим стилем для объявления локальной буферной переменной, помещающей оба оператора в файл.

(defvar xxx ...)                    ;declare xxx with global scope
(make-variable-buffer-local 'xxx)   ;but now make it buffer-local everywhere

Для удобства (defvar-local xxx ...)форму можно использовать как одну строку вместо двух строк выше:

(defvar-local xxx ...)       ;make xxx buffer local everywhere

После объявления, как указано выше, переменная xxx может использоваться как любая другая переменная в операторах setq.

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

(defvar xxx ...)             ;declare xxx with global scope
(make-local-variable 'xxx)   ;make xxx local in this buffer only

Теперь для моих эксплицитных вопросов (все вышеперечисленные были неявными вопросами о том, правильное ли мое понимание).

При установке значения переменных я могу использовать setqили setq-local. Когда следует setq-localиспользовать? Почему?

Что произойдет, если я использую setq-localпеременные локального буфера или локальные переменные без буфера?

Является ли setq-localтребуется для defvar-localобъявленной переменной?

Будет ли setq-localобычная defvarобъявленная переменная превратить ее в локальную переменную буфера? (Другими словами, setq-localкак-то эквивалент (make-variable-local xxx)объявления?

Kevin
источник
Спасибо за дополнительный труд Скотт. С этого момента я добавлю дополнительные обратные кавычки.
Кевин
2
(setq-local VAR VALUE)это просто сокращение (set (make-local-variable VAR) VALUE), которое было (и остается) общей идиомой.
Дрю

Ответы:

18

Большинство ваших предположений близки. Я упомяну несколько позже. Но сначала главный вопрос.

Форма setq-local- это просто удобство, это то же самое, что делать, make-local-variableа затем следовать setq. Если вы C-h f setq-localпросмотрели документацию и нажали на источник, возможно, вы видели это. Вот так я проверил свое первое впечатление. Код немного неясен, так как он оптимизирует такие вещи, как тот факт, что make-local-variableфактически возвращает саму переменную. Использование setq-local- это способ указать на локальность в некотором коде, чтобы другие знали, что код не может играть с глобальным значением переменной. Вам не нужно использовать его для доступа к локальным переменным. Любая переменная может быть сделана локальной для буфера, и это повлияет на весь код, который касается переменной (за исключением случаев, когда она делает все возможное, чтобы получить глобальную копию setq-defaultили аналогичную).

Это основной ответ. Теперь на вашем фоне есть несколько вещей, которые немного отстают. Так как вы спросили об этом, я рассмотрю некоторые вещи.

Первый «не известен другим файлам». Это не совсем правда. Любой код может ссылаться на любую глобальную переменную (поэтому они называются «глобальными»), не включая defvarC-h f defvarговорит так). Фактически, для каждой глобальной переменной должен быть только один основной defvar(со строкой документации и значением по умолчанию). А defvarтолько с именем переменной может использоваться для подавления предупреждения байтового компилятора. Emacs использует только значение из первого, которое видит, и строку документации из последнего. Если есть несколько defvars со значением / docstring, они могут ввести в заблуждение людей, читающих код. И порядок загрузки файлов будет иметь значение.

Некоторые переменные предназначены для использования только в качестве локального буфера, и это те, которые используют defvar-local(или две части defvar/ для make-variable-buffer-localкоторых это сокращение, в основном в старом коде до добавления краткой формы). Это делает его локальным во всех буферах. Для некоторых переменных их основное использование не локально для буфера, но по какой-то причине вы можете захотеть, чтобы один буфер имел другое значение. Это когда вы используете make-local-variable, как правило, в некотором буфере кода установки почти никогда сразу после defvar.

И вообще, у defvarформ есть две цели . Один из них - иметь некоторую документацию, чтобы C-h vдругие могли знать, для чего эта переменная. Второе - объявить значение «по умолчанию», чтобы переменная всегда была установлена. Объявление значения по умолчанию влияет только на глобальный (или используемый по умолчанию) экземпляр переменной и используется только в том случае, если для этого экземпляра еще ничего не установлено. Это означает, что если вы измените setqнекоторую переменную, а затем включите файл defvar(например, через a require), defvar не изменит значение.

¹ Точнее, defvarне изменяет значение переменной, если она уже установлена ​​(однако она устанавливает строку документации); это означает, что файл инициализации пользователя может сделать (setq some-variable)до загрузки пакета, чтобы переопределить значение по умолчанию пакета.

КАРТА
источник
1
Спасибо за очень четкий ответ, это очень помогает. Когда я использовал фразу «не известно другим файлам», я думал о предупреждениях о свободных переменных, потому что эти другие файлы будут думать, что переменная свободна, если я тоже не добавлю в них defvar. Мне нравится ваше заявление, которое set-localговорит читателям, что устанавливается локальная переменная. Все, что я могу сделать, чтобы улучшить читабельность моего кода, хорошо! PS. Я постараюсь чаще переходить к источнику; на моем нынешнем уровне развития, это часто не доходит до семантики ... :-)
Кевин
1
«должен быть только один defvarдля каждой глобальной переменной» не совсем правильно - существуют ситуации, когда (defvar VARNAME) без значения должно быть объявлено, независимо от правильного определения (с начальным значением и в идеале строкой документа) этого VARNAME.
Фил
2
Обратите внимание, что setq-localи defvar-localбыли введены только в Emacs 24.3.
Филс
1
@phils, не могли бы вы указать ситуации, о которых вы говорите, где (defvar varname)следует указывать без значения? Я думаю, это то, что Дрю предложил в другой ветке, когда я спрашивал, как избавиться от предупреждений о свободных переменных в моих файлах для глобалов, которые я объявил в другом месте. Я делаю это сейчас. Это та ситуация, о которой вы говорите?
Кевин
1
Избегание предупреждений о байт-компиляции - одна из причин, да (см. C-h i g (elisp) Warning Tips). Другой - для библиотек, использующих лексическое связывание, чтобы гарантировать (при необходимости), что переменная является динамически связанной, а не лексически связанной.
phils