Apa potensi jebakan yang memungkinkan pengikatan leksikal untuk buffer?

12

Ini terinspirasi oleh diskusi tentang lexical-binding vs lexical-let dalam pertanyaan ini . Karena lexical-binding memberi Anda kemampuan untuk memiliki penutupan yang bermanfaat yang dapat digunakan orang dalam bahasa lain seperti JavaScript mengapa Anda tidak mengaktifkannya sepanjang waktu?

Dengan asumsi kompatibilitas ke belakang dengan Emacsen yang lebih lama bukan masalah jebakan apa yang harus Anda cari jika mengaktifkannya dalam buffer kode lama?

stsquad
sumber

Jawaban:

13

Jebakan utama adalah bahwa semantik yang mengikat untuk variabel yang tidak terdefinisi — yaitu variabel yang tidak didefinisikan dengan defvardan teman — berubah dengan lexical-binding: Tanpa itu, letmengikat segala sesuatu secara dinamis, tetapi dengan lexical-bindingvariabel yang tidak terdefinisi yang terikat terikat secara leksikal , dan bahkan tereliminasi sepenuhnya jika tidak digunakan dalam lingkup leksikal saat ini .

Kode lama terkadang bergantung pada ini. Untuk menghindari dependensi keras untuk fitur opsional, itu akan mengikat variabel dinamis tanpa memerlukan pustaka yang sesuai atau mendeklarasikan variabel itu sendiri:

(let ((cook-eggs-enabled t))
  (cook-my-meal))

Jika fitur memasak adalah opsional, kami tidak ingin memaksakan dependensi yang tidak perlu ke pengguna, jadi kami tidak menggunakan (require 'cook)dan sebaliknya mengandalkan autoloading cook-my-mealfungsi.

Jelas bagi pembaca manusia yang cook-eggs-enabledbukan variabel lokal, tetapi masih merujuk pada beberapa variabel dinamis global dari cookperpustakaan di sini. Tanpa lexical-bindingkode ini berfungsi sebagaimana dimaksud: cook-eggs-enabledterikat secara dinamis, baik didefinisikan atau tidak.

Dengan lexical-bindingbagaimanapun, rusak: cook-eggs-enabledsekarang terikat secara leksikal (dan kemudian dioptimalkan pergi, karena itu tidak digunakan), sehingga variabel global yang dinamis cook-eggs-enabledini tidak pernah tersentuh sama sekali dan masih nilpada saat cook-my-mealdipanggil, jadi kami tidak mengherankan akan memiliki telur setiap dalam makanan kami.

Untungnya, masalah-masalah ini sangat mudah dikenali : Kompiler byte secara alami memperingatkan tentang pengikatan leksikal yang tidak digunakan di sini.

Cara mengatasinya sederhana: Baik menambahkan a (require 'cook)(untuk fitur yang sebenarnya bukan opsional), atau — untuk menghindari dependensi keras — mendeklarasikan variabel tersebut sebagai variabel dinamis dalam kode Anda sendiri . Ada defvarbentuk khusus untuk ini:

(defvar cook-eggs-enabled)

Ini mendefinisikan cook-eggs-enabledsebagai variabel dinamis, tetapi tidak mempengaruhi docstring, load-history(dan dengan demikian find-variabledan teman-teman) atau apa pun, kecuali sifat mengikat variabel.

lunaryorn
sumber
Dalam kasus dinamis, bukankah snipet kode itu menyebabkan cook-eggs-enabledtidak terikat ketika letselesai? Saya cukup yakin saya mengalami bug seperti ini sebelumnya. Defvar terjadi di dalam let, dan letkemudian mengembalikan variabel ke keadaan awal (void).
Malabarba
1
@Malababa Tidak, itu adalah situasi yang berbeda. Lihat paragraf terakhir dari Mendefinisikan Variabel untuk alasannya.
lunaryorn