Apa yang dilakukan `setq-local`, dan kapan saya harus menggunakannya?

16

Saya tidak begitu jelas tentang semua variasi variabel buffer-lokal, bahkan setelah membaca semua dokumen dan banyak posting di SX.

Berikut ringkasan pemahaman saya:

(defvar foo ..)mendeklarasikan variabel dinamis untuk file. Tetapi variabelnya adalah (1) tidak dikenal ke file lain kecuali mereka menyertakan defvar pernyataan juga, dan (2) variabelnya adalah global dalam cakupan, bukan buffer lokal.

(make-variable-buffer-local foo)setelah di defvaratas memberitahu kompiler dan semua orang bahwa variabel foo harus diperlakukan sebagai penyangga-lokal di mana pun diset, ketika diatur. Jadi pola ini adalah gaya yang baik untuk mendeklarasikan variabel buffer-local, meletakkan kedua pernyataan tersebut secara berurutan di dalam file.

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

Untuk kenyamanan, (defvar-local xxx ...)formulir dapat digunakan sebagai satu baris, sebagai ganti dari dua baris di atas:

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

Setelah dinyatakan seperti di atas, variabel xxx dapat digunakan seperti variabel lain dalam pernyataan setq.

Jika saya hanya ingin memiliki satu instance dari variabel buffer-lokal yang sudah menjadi variabel dinamis global, saya akan menggunakan deklarasi berikut. Yang pertama mendeklarasikan variabel dinamis lingkup global, dan pernyataan kedua hanya membuat satu instance dari versi buffer-lokal dari variabel itu, dalam buffer saat ini:

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

Sekarang untuk pertanyaan explict saya (semua pertanyaan di atas adalah pertanyaan implisit tentang apakah pemahaman saya benar).

Saat mengatur nilai variabel, saya bisa menggunakan setqatau setq-local. Kapan sebaiknya setq-localdigunakan? Mengapa?

Apa yang terjadi jika saya menggunakan setq-localvars buffer-lokal, atau vars non-buffer-lokal?

Apakah setq-localdiperlukan untuk defvar-localvariabel yang dideklarasikan?

Akankah setq-localpada defvarvariabel yang dinyatakan normal mengubahnya menjadi variabel buffer-lokal? (Dengan kata lain, apakah setq-localitu setara dengan (make-variable-local xxx)deklarasi?

Kevin
sumber
Terima kasih atas kerja ekstra Scott. Saya akan memasukkan backquote tambahan mulai dari sini
Kevin
2
(setq-local VAR VALUE)hanya singkatan untuk (set (make-local-variable VAR) VALUE), yang (dan masih) idiom umum.
Drew

Jawaban:

18

Sebagian besar asumsi Anda dekat. Saya akan menyebutkan beberapa nanti. Tapi, pertama pertanyaan utamanya.

Bentuknya setq-localhanya kenyamanan, itu sama dengan melakukan make-local-variablediikuti oleh setq. Jika Anda telah melakukan C-h f setq-localuntuk melihat dokumentasi dan mengklik ke sumbernya, Anda mungkin telah melihat ini. Begitulah cara saya memverifikasi kesan pertama saya. Kode sedikit tidak jelas karena mengoptimalkan hal-hal seperti fakta yang make-local-variablesebenarnya mengembalikan variabel itu sendiri. Menggunakan setq-localadalah cara untuk menunjukkan lokalitas dalam beberapa kode, sehingga orang lain tahu kode tidak bisa bermain dengan nilai global variabel. Anda tidak perlu menggunakannya untuk mengakses variabel lokal. Variabel apa pun dapat dibuat sebagai buffer-lokal dan itu akan memengaruhi semua kode yang menyentuh variabel (kecuali jika keluar dari caranya untuk mendapatkan salinan global dengan setq-defaultatau serupa).

Itulah jawaban utama. Sekarang, ada beberapa hal di latar belakang Anda yang sedikit aneh. Karena Anda bertanya tentang itu, saya akan membahas beberapa hal.

Yang pertama adalah "tidak diketahui file lain". Ini tidak benar. Kode apa pun dapat merujuk variabel global apa pun (itu sebabnya mereka disebut "global") tanpa menyertakan defvar(dan C-h f defvarmengatakan demikian). Bahkan, seharusnya hanya ada satu main defvar(dengan nilai docstring dan default) untuk setiap variabel global. A defvardengan hanya nama variabel dapat digunakan untuk menekan peringatan byte-compiler. Emacs hanya menggunakan nilai dari yang pertama dilihatnya - dan docstring dari yang terakhir. Jika ada beberapa defvars dengan nilai / docstring mereka dapat membingungkan orang yang membaca kode. Dan urutan memuat file akan jadi masalah.

Beberapa variabel dimaksudkan untuk digunakan hanya sebagai buffer-lokal dan mereka adalah yang menggunakan defvar-local(atau dua bagian defvar/ make-variable-buffer-localyang merupakan kependekan, sebagian besar dalam kode lama sebelum formulir pendek ditambahkan). Ini membuatnya menjadi buffer-lokal di semua buffer. Untuk beberapa variabel, penggunaan utamanya bukan buffer-local, tetapi untuk beberapa alasan Anda mungkin ingin satu buffer memiliki nilai yang berbeda. Saat itulah Anda menggunakan make-local-variable, biasanya dalam beberapa kode pengaturan buffer hampir tidak pernah tepat setelah defvar.

Dan sebagai hal yang umum, ada dua tujuan untuk defvarformulir. Salah satunya adalah memiliki beberapa dokumentasi, sehingga C-h vdapat digunakan oleh orang lain untuk mengetahui untuk apa variabel tersebut. Yang kedua adalah mendeklarasikan nilai "default", sehingga variabel selalu disetel. Deklarasi nilai default hanya mempengaruhi instance global (atau default) variabel, dan hanya digunakan jika instance itu belum disetel ke sesuatu. Itu berarti bahwa jika Anda setqbeberapa variabel dan kemudian memasukkan file dengan defvar(misalnya melalui a require), defvar tidak akan mengubah nilai.

¹ Lebih tepatnya, defvartidak mengubah nilai variabel jika sudah diatur (itu memang mengatur docstring); ini dimaksudkan agar file init pengguna dapat melakukan (setq some-variable)sebelum paket dimuat untuk mengganti nilai default paket.

PETA
sumber
1
Terima kasih atas jawaban yang sangat jelas, ini sangat membantu. Ketika saya menggunakan frasa "tidak dikenal ke file lain" saya sedang memikirkan peringatan variabel bebas karena file-file lain akan berpikir variabel itu gratis kecuali saya menambahkan defvar ke mereka juga. Saya suka pernyataan Anda yang set-localmemberi tahu pembaca bahwa var lokal sedang diset. Apa pun yang dapat saya lakukan untuk meningkatkan keterbacaan kode saya adalah baik! PS. Saya akan mencoba mengklik ke sumber lebih sering; pada tingkat perkembangan saya saat ini, sering kali tidak benar-benar mencapai semantik ... :-)
Kevin
1
"seharusnya hanya ada satu defvaruntuk setiap variabel global" tidak sepenuhnya benar - ada situasi ketika (defvar VARNAME) tanpa nilai harus dinyatakan, terlepas dari definisi yang tepat (dengan nilai awal dan idealnya adalah sebuah dokumentasi) dari VARNAME itu.
phils
2
Perhatikan juga bahwa setq-localdan defvar-localhanya diperkenalkan di Emacs 24.3.
phils
1
@phils, bisakah Anda mengidentifikasi situasi yang Anda bicarakan, di mana (defvar varname)harus dinyatakan tanpa nilai? Saya berpikir itulah yang disarankan Drew di utas lain ketika saya bertanya bagaimana cara menghilangkan peringatan variabel gratis di file saya untuk global yang telah saya nyatakan di tempat lain. Saya melakukannya sekarang. Apakah itu situasi yang Anda bicarakan?
Kevin
1
Menghindari peringatan kompilasi byte adalah salah satu alasannya, ya (lihat C-h i g (elisp) Warning Tips). Yang lainnya adalah untuk perpustakaan yang menggunakan lexical-binding, untuk memastikan (jika perlu) bahwa suatu variabel terikat secara dinamis daripada terikat secara leksikal.
phils