Jebakan utama adalah bahwa semantik yang mengikat untuk variabel yang tidak terdefinisi — yaitu variabel yang tidak didefinisikan dengan defvar
dan teman — berubah dengan lexical-binding
: Tanpa itu, let
mengikat segala sesuatu secara dinamis, tetapi dengan lexical-binding
variabel 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-meal
fungsi.
Jelas bagi pembaca manusia yang cook-eggs-enabled
bukan variabel lokal, tetapi masih merujuk pada beberapa variabel dinamis global dari cook
perpustakaan di sini. Tanpa lexical-binding
kode ini berfungsi sebagaimana dimaksud: cook-eggs-enabled
terikat secara dinamis, baik didefinisikan atau tidak.
Dengan lexical-binding
bagaimanapun, rusak: cook-eggs-enabled
sekarang terikat secara leksikal (dan kemudian dioptimalkan pergi, karena itu tidak digunakan), sehingga variabel global yang dinamis cook-eggs-enabled
ini tidak pernah tersentuh sama sekali dan masih nil
pada saat cook-my-meal
dipanggil, 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 defvar
bentuk khusus untuk ini:
(defvar cook-eggs-enabled)
Ini mendefinisikan cook-eggs-enabled
sebagai variabel dinamis, tetapi tidak mempengaruhi docstring, load-history
(dan dengan demikian find-variable
dan teman-teman) atau apa pun, kecuali sifat mengikat variabel.
cook-eggs-enabled
tidak terikat ketikalet
selesai? Saya cukup yakin saya mengalami bug seperti ini sebelumnya. Defvar terjadi di dalamlet
, danlet
kemudian mengembalikan variabel ke keadaan awal (void).