Defvar kapsam belirleme neden bir girişim olmadan farklı çalışır?


10

elisp-defvar-test.elİçeren adında bir dosyam olduğunu varsayalım :

;;; elisp-defvar-test.el ---  -*- lexical-binding: t -*- 

(defvar my-dynamic-var)

(defun f1 (x)
  "Should return X."
  (let ((my-dynamic-var x))
    (f2)))

(defun f2 ()
  "Returns the current value of `my-dynamic-var'."
  my-dynamic-var)

(provide 'elisp-dynamic-test)

;;; elisp-defvar-test.el ends here

Bu dosyayı yüklemek ve sonra sıfırdan arabellek gidin ve çalıştırın:

(setq lexical-binding t)
(f1 5)
(let ((my-dynamic-var 5))
  (f2))

(f1 5)gövdesinin beklendiği gibi dinamik olarak kapsamlandırılmış bir değişken olarak f1davrandığını gösteren 5'i beklendiği gibi döndürür my-dynamic-var. Ancak son form, my-dynamic-varbu değişken için sözcüksel kapsam belirleme kullandığını belirten bir geçersiz değişken hatası verir . Bu, aşağıdakilerle ilgili belgelerle çelişiyor gibi görünüyor defvar:

defvarForm Ayrıca sürekli dinamik bile bağlı olduğu böylece, "özel" olarak değişken bildirir lexical-bindingt dir.

defvarTest dosyasındaki formu bir başlangıç ​​değeri sağlayacak şekilde değiştirirsem, değişken belgelerin söylediği gibi her zaman dinamik olarak ele alınır. Bir değişkenin kapsamının neden defvarbu değişkeni bildirirken bir başlangıç ​​değeri sağlayıp sağlamadığı ile belirlendiğini açıklayabilir mi?

Önemli olması durumunda hata geri izlemesi:

Debugger entered--Lisp error: (void-variable my-dynamic-var)
  f2()
  (let ((my-dynamic-var 5)) (f2))
  (progn (let ((my-dynamic-var 5)) (f2)))
  eval((progn (let ((my-dynamic-var 5)) (f2))) t)
  elisp--eval-last-sexp(t)
  eval-last-sexp(t)
  eval-print-last-sexp(nil)
  funcall-interactively(eval-print-last-sexp nil)
  call-interactively(eval-print-last-sexp nil nil)
  command-execute(eval-print-last-sexp)

4
18059 numaralı hatanın tartışmasının alakalı olduğunu düşünüyorum .
Fesleğen

Harika bir soru ve evet, lütfen 18059 numaralı hatanın tartışmasına bakın.
Drew

Anlıyorum, bu yüzden belgeler Emacs 26'da buna değinmek için güncellenecek gibi görünüyor.
Ryan C. Thompson

Yanıtlar:


8

Neden ikisine farklı davranılıyor, çoğunlukla “çünkü ihtiyacımız olan şey bu”. Daha spesifik olarak, tek argüman formu defvaruzun zaman önce ortaya çıktı, ancak diğerinden daha sonra ortaya çıktı ve temel olarak derleyici uyarılarını susturmak için bir "hack" idi: yürütme zamanında hiç bir etkisi olmadı, yani bir "kaza" susturma davranışının (defvar FOO)yalnızca geçerli dosyaya uygulandığını (derleyicinin böyle bir defvarın başka bir dosyada yürütüldüğünü bilmesinin bir yolu olmadığını).

Ne zaman lexical-bindingEmacs'ın-24'de tanıtıldı, biz karar yeniden kullanmak bu (defvar FOO)formu, ama bu şimdi ima yapar bir etkiye sahiptir.

Kısmen önceki "yalnızca geçerli dosyayı etkiler" davranışını korumak, ancak daha da önemlisi, bir kütüphanenin, totodiğer kütüphanelerin totosözlüksel olarak kapsamlandırılmış bir var olarak kullanılmasını engellemeden dinamik olarak kapsamlandırılmış bir var olarak kullanılmasına izin vermek (genellikle paket öneki adlandırma kuralı bunlardan kaçınır) çakışıyor, ancak her yerde ne yazık ki kullanılmıyor), yeni davranışı (defvar FOO)yalnızca geçerli dosyaya uygulanacak şekilde tanımlandı ve hatta yalnızca geçerli kapsam için geçerli olacak şekilde rafine edildi (örneğin, bir işlev içinde görünüyorsa, yalnızca bu işlev içinde var olan).

Temelde (defvar FOO VAL)ve (defvar FOO)sadece iki "tamamen farklı" şeydir. Sadece tarihsel nedenlerle aynı anahtar kelimeyi kullanıyorlar.


1
Cevap için +1. Ancak Common Lisp'in yaklaşımı daha açık ve daha iyi, IMHO.
Drew

@ Çekildi: Çoğunlukla katılıyorum, ancak (defvar FOO)yeni modu eski kodla çok daha uyumlu hale getiriyor. Ayrıca, IIRC'nin CommonLisp'in çözümü ile ilgili bir problem, Elisp'inki gibi saf bir tercüman için oldukça maliyetli olmasıdır (örneğin, her bir değerlendirdiğinizde, bazı değişkenleri etkileyen letbir durum olması durumunda vücudunun içine bakmanız gerekir declare).
Stefan

Her iki konuda da anlaşmaya varıldı.
Drew

4

Denemelere dayanarak, konunun (defvar VAR)hiçbir init değeri olmadan sadece göründüğü kütüphaneler üzerinde bir etkisi olduğuna inanıyorum .

I ilave zaman (defvar my-dynamic-var)için *scratch*tampon, hata artık oluştu.

Başlangıçta bunun bu formu değerlendirmeyi düşündüğünü düşündüm , ancak daha sonra bu form ile dosyayı ziyaret etmenin yeterli olduğunu fark ettim ; ve ayrıca, sadece tampon içinde bu formu eklemenin (veya kaldırmanın), değerlendirmeden (let ((my-dynamic-var 5)) (f2)), aynı tamponun içinde değerlendirilirken neler olduğunu değiştirmek yeterliydi eval-last-sexp.

(Burada neler olduğuna dair gerçek bir anlayışım yok. Davranışı şaşırtıcı buluyorum, ancak bu işlevin nasıl uygulandığına dair ayrıntılardan emin değilim.)

Bu biçimdeki ben katacak defvar(hayır init değeriyle) Derlenmekte elisp dosyasında dışarıdan tanımlı dinamik değişkenin kullanımları hakkında şikayet gelen bayt derleyici önler ancak kendi başına o değil o değişken olmasına neden boundp; bu yüzden değişkeni kesin olarak tanımlamaz. (Değişken eğer unutmayın oldu boundp o zaman bu mesele bütün meydana olmaz.)

Uygulamada bu sen şartıyla Tamam iş dışarı gidiyor varsayalım do şunlardır (defvar my-dynamic-var)sizin kullanan tüm sözcük bağlayıcı kütüphanede my-dynamic-var(muhtemelen başka bir yerde gerçek bir tanımını olurdu) değişken.


Düzenle:

Yorumlarda @ npostavs'tan gelen işaretçi sayesinde:

Hem eval-last-sexpve eval-defunkullanım eval-sexp-add-defvarsamacıyla için:

EXP'yi defvararabellekten önce gelen tüm s ile başa getirin .

Özellikle defvar, tüm defconstve defcustomörneklerini bulur . (Yorum yaparken bile fark ettim.)

Bu, arama zamanında arabelleği aradığından, bu formların değerlendirilmeden bile arabellekte nasıl bir etki yaratabileceğini açıklar ve formun aynı elisp dosyasında (ve değerlendirilmekte olan koddan daha önce) görünmesi gerektiğini doğrular. .



2
eval-sexp-add-defvarsArabellek metninde defvars olup olmadığını kontrol eder.
npostavs

1
+1. Açıkçası bu özellik net değildir veya kullanıcılara açıkça sunulmamaktadır. 18059 hatası için doc düzeltmesi yardımcı olur, ancak bu hala kullanıcılar için kırılgan olmasa bile gizemli bir şeydir.
Drew

0

Bunu hiç çoğaltamıyorum, ikinci pasajı değerlendirmek burada gayet iyi çalışıyor ve beklendiği gibi 5 döndürüyor. my-dynamic-varKendi başına değerlendirme yapmadığınızdan emin misiniz ? Bu, değişken geçersiz olduğu için bir değer atar, bir değere ayarlanmamıştır ve yalnızca dinamik olarak bir tanesine bağlarsanız bir tane olur.


1
lexical-bindingFormları değerlendirmeden önce sıfır olmayan bir değer belirlediniz mi? lexical-bindingNil ile tanımladığınız davranışı alıyorum , ancak bunu non-nil olarak ayarladığımda void-değişken hatası alıyorum.
Ryan C. Thompson

Evet, bunu ayrı bir dosyaya kaydettim, geri döndü, lexical-bindingayarlandığını ve formları sırayla değerlendirdiğini kontrol ettim .
wasamasa

@wasamasa Benim için çoğaltır, belki my-dynamic-varşu anki oturumunuzda yanlışlıkla üst düzey bir dinamik değer verdiniz ? Bence bu kalıcı olarak özel olabilir.
npostavs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.