'Nilai simbol sebagai variabel tidak valid' dalam panggilan balik dari url-ambil

8

Saat menjalankan yang berikut ini saya mendapatkan kesalahan:

(defun caller (func)
  (url-retrieve "http://m0smith.freeshell.org/"
   (lambda (status) (funcall func))))


(caller (lambda() (message "called")))

Hasil:

error in process filter: Symbol's value as variable is void: func

Apa cara terbaik untuk mengatasi masalah ini? Pada dasarnya saya perlu menerima panggilan balik dari tempat lain, membungkusnya dengan lambda lain dan menggunakannya sebagai panggilan balik untuk mengambil-url.

Jika saya mengubah penelepon ke

(defun caller (func)
  (url-retrieve "http://m0smith.freeshell.org/"
   `(lambda (status) (funcall ,func))))

berhasil. Namun, saya tidak bisa melakukan ini karena func sedang dilewati oleh flycheck dan ekspansi makro memecahnya. Untuk melihat konteks lengkap dari apa yang saya lakukan: https://gist.github.com/m0smith/b5961fda6afd71e82983

M Smith
sumber
Apa ekspansi makro? Paragraf terakhir Anda tidak jelas. Posting di sini penjelasan lengkap tentang masalahnya. Menggunakan backquote dengan koma adalah solusinya (satu solusi). Yang lain bisa menggunakan lexical-letatau menyetel variabel lexical-binding. Harap jelaskan apa masalahnya dengan "makro" yang tidak ditampilkan.
Drew
Saya berasumsi `dan makro diperluas. Apa pun bentuk itu disebut itu. Saya ingin bekerja di EMACS 23. Apakah ada fitur leksikal yang tersedia?
M Smith
Jika tidak ada penggunaan di FUNCluar funcallmaka secara logis Anda tidak perlu mengikat leksikal di sini. Tidak ada yang salah dengan menggunakannya, tetapi Anda tidak memerlukannya , kecuali beberapa kode benar-benar perlu menggunakan variabel FUNC . Jika Anda tidak membutuhkannya (yang terlihat seperti sejauh ini), maka ganti saja kemunculannya dengan nilainya, menggunakan backquote dengan koma.
Drew
Pengikatan leksikal tersedia di Emacs 23 (dan sebelumnya), menggunakan lexical-let. Variabel global lexical-bindingtersedia dalam Emacs 24.
Drew

Jawaban:

5

Anda dapat mencapai ini secara lokal dengan menggunakan lexical-let cl.el :

(eval-when-compile '(require 'cl))

(defun my-test-caller (func)
  (lexical-let ((ext-func func))
    (url-retrieve "http://www.google.com"
                  (lambda (status) (funcall ext-func)))))

(my-test-caller #'(lambda() (message "called")))

Secara eksplisit seperti yang dikatakan bantuan:

Like `let', but lexically scoped.
The main visible difference is that lambdas inside BODY will create
lexical closures as in Common Lisp.

Sekarang Anda bisa mendapatkan efek yang sama dengan mengaktifkan lexical-binding yang ditambahkan di Emacs 24.1. Ini adalah variabel lokal buffer yang dapat diatur dan akan memungkinkan penutupan leksikal pada semua kode dalam buffer. Jadi buffer Anda akan terlihat seperti ini:

;; -*- lexical-binding: t; -*-

(defun my-lexical-test-caller (func)
  (url-retrieve "http://www.google.com"
                (lambda (status) (funcall func))))

(my-lexical-test-caller
 #'(lambda()
     (message "called from lexical-binding def")))
stsquad
sumber
Terima kasih. Namun saya mendapatkan my-test-caller: Symbol's function definition is void: lexical-letdi emacs saya: GNU Emacs 24.4.1 (x86_64-w64-mingw32) `dari 2014-10-20 di KAEL
M Smith
@ MSmith - ahh add (wajib 'cl)
stsquad
lexical-letdidefinisikan dalam cl-macs.el. Jadi(eval-when-compile '(require 'cl))
Drew
Anda tidak perlu meminta cl.elsaat runtime, hanya untuk ini. lexical-letadalah makro, jadi cukup untuk membutuhkannya pada waktu kompilasi.
Drew
2
Tolong jangan. Gunakan lexical-binding. Flycheck tidak mendukung Emacs 23, jadi tidak ada gunanya mencoba menjadi kompatibel dengan itu.
lunaryorn
5

Aktifkan lexical-bindinguntuk perpustakaan Anda, dengan M-x add-file-local-variable-prop-line RET lexical-binding RET t.

Tolong jangan gunakan lexical-letseperti yang disarankan oleh jawaban lain. Flycheck sendiri tidak kompatibel dengan Emacs 23, jadi tidak ada gunanya mencoba mempertahankan kompatibilitas Emacs 23 dalam kode Anda sendiri.

lunaryorn
sumber
Terima kasih. Itu akan membantu jadi saya tidak mencoba membuat emac yang lebih tua bekerja tanpa alasan
M Smith
Apa yang salah dengan menggunakan lexical-let for this?
stsquad
@stsquad Lebih lambat dan lebih bertele-tele. Dengan lexical-bindingtidak ada kebutuhan untuk mengikat tambahan, karena argumen itu sendiri dibatasi secara leksikal. Selain itu, lexical-bindingmembuat penutupan benar, sedangkan lexical-letmenggunakan simbol yang tidak diinginkan untuk meniru pengikatan leksikal.
lunaryorn
@ Lunaryorn: apakah tidak ada risiko dengan mengaktifkan lexical-binding pada buffer kode legacy yang ada, Anda mungkin mendapatkan efek yang tidak terduga? Lagi pula saya telah memperluas jawaban saya untuk menyebutkan kedua solusi.
stsquad
@stsquad Ya, mungkin ada efek tak terduga dalam kode lawas yang ditulis dengan buruk yang bergantung pada letpengikatan variabel yang tidak terdefinisi secara dinamis. Tetapi sekali lagi, Flycheck adalah untuk Emacs 24, jadi kita tidak berbicara tentang kode warisan.
lunaryorn