Singkirkan referensi ke peringatan kompilasi byte variabel gratis

Saya sedang menulis mode utama emacs, yang menggunakan variabel buffer-lokal untuk menyimpan beberapa status:

(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"))

Ini berfungsi dengan sangat baik dan memiliki properti bahwa dalam buffer apa pun yang tidak menggunakan mode utama saya, variabel foo-state tidak terikat (yang menurut saya merupakan hal yang baik, karena menghindari kekacauan tabel simbol).

Namun, kompilasi byte potongan kode tersebut menghasilkan peringatan berikut:

Warning: assignment to free variable `foo-state'

Menggunakan defvar menghilangkan peringatan, tetapi memiliki efek samping bahwa foo-state sekarang terikat di mana-mana, yang menurut saya tidak diinginkan.

Apakah ada cara untuk menghilangkan peringatan sambil tetap tidak mengikat variabel khusus mode di setiap buffer? Atau apakah saya salah ketika menganggap variabel-variabel ini tidak boleh dideklarasikan secara global?


person François Févotte    schedule 14.09.2012    source sumber
comment
itu adalah konversi jawaban-ke-komentar yang sangat tidak membantu oleh seseorang. Agar hal di atas dapat dibaca kembali: C-h i g (elisp) Warning Tips RET adalah tempat dokumentasi yang menjawab pertanyaan ini berada.   -  person phils    schedule 08.04.2014
comment
@phils Ya. Saya, misalnya, menyukai jawaban Anda. Terima kasih!   -  person François Févotte    schedule 08.04.2014
comment
BTW, saya sarankan Anda menggunakan define-derived-mode, seperti pada (define-derived-mode foo-mode nil "foo" "My nice major mode." (set (make-local-variable 'foo-state) "bar")).   -  person Stefan    schedule 16.04.2014
comment
Awas! Bagian Warning Tips di Manual Emacs berisi kebohongan terang-terangan mengenai defvar: Definisi seperti itu tidak berpengaruh kecuali memberi tahu kompiler agar tidak memperingatkan tentang penggunaan variabel [...] dalam file ini.. Hal ini tentu saja dapat berdampak lebih besar daripada sekadar membungkam kompiler: [itu] juga mendeklarasikan variabel sebagai variabel khusus, sehingga selalu terikat secara dinamis meskipun 'pengikatan leksikal' adalah t. (kutipan diambil dari dokumentasi sebenarnya untuk defvar). Mungkin saran di Warning Tips ditulis sebelum pengenalan pengikatan leksikal di Emacs Lisp...   -  person ack    schedule 01.03.2018


Jawaban (2)


Cara resmi untuk melakukan apa yang Anda inginkan adalah (defvar foo-state). Perhatikan tidak adanya argumen kedua. Perhatikan juga bahwa deklarasi seperti itu hanya berlaku pada file tempat ia ditemukan (atau pada cakupan di mana ia ditemukan, jika digunakan di dalam suatu fungsi).

person Stefan    schedule 15.09.2012
comment
Terima kasih. Membaca ulang dokumentasi defvar memperjelas: jika INITVALUE tidak ada, nilai SYMBOL tidak disetel. Saya melewatkan ini dan selalu memberikan argumen kedua (yang menyebabkan variabel selalu terikat). - person François Févotte; 15.09.2012

Deklarasikan variabel dengan defvar. Tidak ada cara lain untuk menghilangkan peringatan tersebut, dan ini benar-benar dianggap sebagai praktik yang baik.

Niat Anda untuk menjaga tabel simbol tetap rapi adalah hal yang baik, tetapi Anda tidak benar-benar melakukannya. Saya pikir Anda telah salah memahami semantik pengikatan variabel di Emacs Lisp, karena Anda tampaknya percaya bahwa dengan tidak mendeklarasikannya foo-state akan dibatalkan ikatannya di buffer apa pun yang tidak menggunakan foo-mode. Bukan itu masalahnya.

Dalam nama Emacs Lisp (alias simbol) bersifat global. Segera setelah foo-state dievaluasi pertama kali, runtime membuat objek simbol baru untuk foo-state dan memasukkannya ke dalam tabel simbol global (alias obarray). Tidak ada tabel simbol lokal, jadi tidak masalah di mana foo-state dievaluasi dan bagaimana caranya, foo-state mengacu pada objek simbol yang sama di tempat mana saja (lihat Membuat Simbol).

Setiap objek simbol terdiri dari komponen (alias sel), salah satunya adalah sel variabel (lihat Komponen simbol). setq memodifikasi pengikatan sistem saat ini, pada tingkat atas tanpa pengikatan leksikal ini secara efektif mengubah sel variabel dari objek simbol, sehingga nilai global dari variabel tersebut. Sekali lagi, tidak masalah di mana setq dievaluasi. Sebenarnya jika beberapa bar-mode dievaluasi (setq foo-state "bar"), foo-state akan terikat ke "bar" di foo-mode juga, dan sebaliknya.

Jadi satu-satunya efek (defvar) atas (setq) adalah mendokumentasikan niat menggunakan simbol sebagai variabel global, jadi memberitahu orang lain untuk tidak mengubah variabel ini kecuali memang dimaksudkan untuk memanipulasi perilaku foo-mode. Anda dapat melampirkan dokumentasi ke variabel, dan menandainya sebagai variabel yang didefinisikan dalam buffer Anda (C-h v foo-state akan memberikan tautan untuk melompat ke definisi tersebut).

Karena Emacs Lisp tidak memiliki namespace dan – secara default – memiliki cakupan dinamis, dokumentasi pada dasarnya penting untuk menghindari konflik antar modul. Jika saya menulis bar-mode menggunakan foo-mode Anda, saya mungkin secara tidak sengaja mengikat ke foo-state, memanggil foo-change-state dan kemudian melihat mode saya berperilaku buruk karena suatu variabel tidak sengaja ditimpa. Mendeklarasikan foo-state tidak membuat hal ini menjadi mustahil, tetapi setidaknya memungkinkan saya untuk menangkap kesalahan tersebut, karena C-h v foo-state akan mengungkapkan bahwa variabel ini digunakan oleh mode lain, jadi sebaiknya saya tidak menggunakannya kecuali saya benar-benar bermaksud untuk memanipulasi mode itu.

Sebagai kata terakhir: Dalam semua teks "mode" yang disebutkan di atas dapat diganti dengan file Emacs Lisp. modes tidak ada yang istimewa dalam hal simbol. Semua hal di atas juga berlaku untuk Emacs Lisp yang tidak mendeklarasikan mode, tetapi hanya berisi banyak fungsi.

person lunaryorn    schedule 14.09.2012
comment
Tidak benar bahwa foo-state tidak akan dilepas ikatannya di buffer lain — coba saja contoh poster. Meskipun obarray bersifat global, nilai simbol buffer-lokal diubah pada setiap switch buffer, yang dapat mencakup perubahan ke status tidak terikat. Jika Anda menekan M-: foo-state RET di buffer foo-mode, Anda akan mendapatkan "bar", sedangkan jika Anda melakukan hal yang sama di buffer lain, Anda akan mendapatkan kesalahan. Meskipun hal ini tidak bersifat idiomatis di elisp, hal ini pasti dapat diperoleh terlepas dari tabel simbol globalnya. - person user4815162342; 15.09.2012
comment
Perhatikan juga bahwa, karena Emacs Lisp tidak memiliki sistem modul, bukan dokumentasi yang melindungi Anda dari bentrokan variabel global yang tidak disengaja, tetapi konvensi penggunaan awalan simbol. Inilah sebabnya setiap mode mengawali semua simbolnya dengan mode-. defvar hanya mengijinkan kompiler untuk memancarkan (lebih tepatnya, tidak memancarkan) peringatan yang menangkap tugas yang salah eja, peringatan yang sama yang merupakan positif palsu dalam kasus pembuat poster. - person user4815162342; 15.09.2012
comment
Jika foo-state disetel oleh buffer lain sebelum foo-mode mendeklarasikannya sebagai buffer lokal, ia mendapat nilai global. Hanya setelah variabel buffer lokal dideklarasikan, pengikatan buffer-lokal mulai berlaku. Dan meskipun demikian pengikatan global masih dapat diubah melalui setq-default, meskipun hal ini tentu saja tidak akan mempengaruhi pengikatan lokal penyangga. - person lunaryorn; 15.09.2012
comment
Awalan simbol membantu menghindari konflik, namun tidak mencegahnya, terutama karena variabel khusus yang dideklarasikan oleh mode pemrograman sering kali memiliki nama yang ada di mana-mana hanya karena awalan simbol (yaitu nama bahasa pemrograman) ada di mana-mana. Misalnya, ruby-mode dapat mendeklarasikan variabel ruby-executable untuk evaluasi buffer melalui penerjemah Ruby, namun perpustakaan lain yang berhubungan dengan Ruby (yaitu pemeriksa flymake untuk Ruby) mungkin juga dengan mudah menggunakan variabel ini, karena ini hanyalah nama yang paling alami untuk dipilih. ini. Itu sebabnya dokumentasi juga diperlukan… - person lunaryorn; 15.09.2012
comment
Jadi singkatnya, apakah di elisp semuanya hanya variabel global? Memodifikasi satu tempat akan mengakibatkan semua eksekusi berikut menggunakan nilai baru? Penjelasan yang sangat bagus! - person Hot.PxL; 24.05.2015