Defun di dalam biarkan dengan lexical binding memberikan byte-compile warning "fungsi tidak diketahui didefinisikan"

13

Saya ingin mendapatkan efek dari variabel statis dengan menggunakan defunbagian dalam letdengan pengikatan leksikal untuk membuat penutupan. Namun, saat byte-compile file, saya mendapat peringatan. Apakah saya melakukan sesuatu yang salah, atau jika tidak, adakah cara untuk menekan peringatan ini?

Saya telah membuat MCVE:

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

(let ((count 0))
  (defun increase-count ()
    (interactive)
    (setq count (1+ count))
    (message "Count is: %d" count))

  ;; The warning happens here.
  (increase-count))

Kode berfungsi seperti yang diharapkan: fungsi increase-countmencetak "Hitung adalah: n" di mana n bertambah setiap kali dipanggil. Namun, ketika byte-compiling file ini, saya mendapatkan peringatan berikut:

In end of data:
mcve.el:11:1:Warning: the function ‘increase-count’ is not known to be
    defined.

Sepertinya saya yang increase-countharus selalu didefinisikan sebelum dipanggil pada akhir blok let. Bukankah ini masalahnya?

Will Kunkel
sumber
defuntidak melakukan apa yang Anda pikirkan, ia selalu menciptakan definisi tingkat atas. Elisp memang bukan Skema ...
wasamasa
2
Saya sadar bahwa itu menciptakan definisi tingkat atas; itu yang saya mau. Saya hanya ingin definisi tingkat atas itu menjadi penutup. Tampaknya berfungsi seperti yang saya inginkan, kecuali untuk peringatan byte-compilation ini.
Will Kunkel

Jawaban:

7

Cara byte-compiler untuk memutuskan apakah suatu fungsi akan didefinisikan atau tidak sangat "naif" dan tertipu bahkan dalam kasus "jelas" Anda. Tetapi Anda dapat menulisnya dengan cara yang memungkinkan kompiler memahami apa yang terjadi:

(defalias 'increase-count
  (let ((count 0))
    (lambda ()
      (interactive)
      (setq count (1+ count))
      (message "Count is: %d" count))))

Tentu saja, yang lebih baik adalah meningkatkan logika byte-compiler: patch diterima untuk itu.

Stefan
sumber
5

Untuk menekan peringatan byte-compiler, coba tambahkan ini sebelum kode Anda, mulai dari kolom 0 (paling kiri):

(declare-function increase-count "your-file-name.el")

C-h f declare-function memberitahumu:

declare-functionadalah makro Lisp di subr.el.

(declare-function FN FILE &optional ARGLIST FILEONLY)

Beri tahu byte-compiler bahwa fungsi FNdidefinisikan, dalam FILE. The FILEArgumen tidak digunakan oleh byte-compiler, tetapi oleh check-declarepaket, yang cek yang FILE berisi definisi untuk FN.

FILEdapat berupa file Lisp (dalam hal ini ".el" ekstensi bersifat opsional), atau file C. File C diperluas relatif ke "src/"direktori Emacs . File-file Lisp dicari untuk digunakan locate-library, dan jika gagal mereka diperluas relatif ke lokasi file yang berisi deklarasi. A FILEdengan "ext:"awalan adalah file eksternal. check-declareakan memeriksa file seperti itu jika ditemukan, dan melewatkannya tanpa kesalahan jika tidak.

Opsional ARGLISTmenentukan FNargumen, atau tuntuk tidak menentukan FNargumen. ARGLISTDefault yang dihilangkan untuk t, bukan nil: a nil ARGLISTmenetapkan daftar argumen kosong, dan eksplisit t ARGLISTadalah pengganti yang memungkinkan memasok argumen selanjutnya.

Opsional FILEONLYnon- nilsarana yang check-declareakan memeriksa hanya yang FILEada, bukan yang ditentukan FN. Ini dimaksudkan untuk definisi fungsi yang check-declaretidak mengenali, misalnya defstruct,.

Perhatikan bahwa untuk keperluan check-declare, pernyataan ini harus merupakan spasi non-spasi pertama pada suatu baris.

Untuk informasi lebih lanjut, lihat Info node (elisp)Declaring Functions.

Drew
sumber
Apakah FILEONLYargumen nihil diperlukan untuk kasus ini? BTW, saya akan memberikan jawaban yang sama ;-).
Tobias
@Tobias: FILEONLYsepertinya tidak diperlukan di sini, untukku. Yang tampaknya menunjukkan yang check-declaremengakui fdan gmenantang.
Drew
@Rew, saya pikir itu komentar terakhir tentang fdan ghanya masuk akal dalam konteks emacs.stackexchange.com/q/39439 ?
phils
@phils: Ya, saya bermaksud mengatakan ini: FILEONLYsepertinya tidak diperlukan di sini, untuk saya. Yang tampaknya menunjukkan yang check-declaremengakui increase-countpertahanan. ;-)
Drew
3

Saya percaya menempatkan definisi tersebut di dalam eval-and-compilejuga akan secara dangkal mencapai hasil yang sama seperti dalam jawaban yang benar Stefan :

(eval-and-compile
  (let ((count 0))
    (defun increase-count ()
      (interactive)
      (setq count (1+ count))
      (message "Count is: %d" count))))

Saya, bagaimanapun, hampir tidak terbiasa dengan kehalusan penggunaan eval-and-compiledan, lebih jauh, jangan berharap pendekatan ini dengan cara apa pun lebih unggul.

Kemangi
sumber