Defun inside let avec lexical binding donne un avertissement de compilation d'octets "la fonction n'est pas connue pour être définie"

13

Je veux obtenir l'effet d'une variable statique en utilisant defuninside of letavec une liaison lexicale pour créer une fermeture. Cependant, lors de la compilation d'octets du fichier, je reçois un avertissement. Suis-je en train de faire quelque chose de mal, ou sinon, existe-t-il un moyen de supprimer cet avertissement?

J'ai créé un 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))

Le code fonctionne comme prévu: la fonction increase-countaffiche "Count is: n" où n augmente à chaque appel. Cependant, lors de la compilation d'octets de ce fichier, j'obtiens l'avertissement suivant:

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

Il me semble que cela increase-countdevrait toujours être défini avant d'être appelé à la fin du let-block. Ce n'est pas le cas?

Will Kunkel
la source
defunne fait pas ce que vous pensez qu'il fait, il crée toujours une définition de haut niveau. Elisp n'est après tout pas Scheme ...
wasamasa
2
Je suis conscient que cela crée une définition de haut niveau; C'est ce que je veux. Je veux juste que cette définition de haut niveau soit une clôture. Il semble fonctionner comme je le souhaite, à l'exception de cet avertissement de compilation d'octets.
Will Kunkel

Réponses:

7

La façon dont le compilateur d'octets décide si une fonction sera définie ou non est très "naïve" et est dupe même dans votre cas "évident". Mais vous pouvez l'écrire d'une manière qui permet au compilateur de comprendre ce qui se passe:

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

Bien sûr, encore mieux serait d'améliorer la logique du compilateur d'octets: les correctifs sont les bienvenus pour cela.

Stefan
la source
5

Pour supprimer l'avertissement du compilateur d'octets, essayez de l'ajouter avant votre code, en commençant dans la colonne 0 (à l'extrême gauche):

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

C-h f declare-function vous indique:

declare-functionest une macro Lisp subr.el.

(declare-function FN FILE &optional ARGLIST FILEONLY)

Dites au compilateur d'octets que la fonction FNest définie, dans FILE. L' FILEargument n'est pas utilisé par le compilateur d'octets, mais par le check-declarepackage, qui vérifie que FILE contient une définition pour FN.

FILEpeut être soit un fichier Lisp (auquel cas l' ".el" extension est facultative), soit un fichier C. Les fichiers C sont développés par rapport au "src/"répertoire Emacs . Les fichiers Lisp sont recherchés à l'aide de locate-library, et si cela échoue, ils sont développés par rapport à l'emplacement du fichier contenant la déclaration. Un FILEavec un "ext:"préfixe est un fichier externe. check-declarevérifiera ces fichiers s'ils sont trouvés et les ignorera sans erreur s'ils ne le sont pas.

Facultatif ARGLISTspécifie FNles arguments de ou tne spécifie pas FNles arguments de. Un omis ARGLISTest par défaut t, not nil: a nil ARGLISTspécifie une liste d'arguments vide, et un explicite t ARGLISTest un espace réservé qui permet de fournir un argument ultérieur.

Option FILEONLYnon des nilmoyens qui check-declarevérifieront seulement FILEexiste, non pas qu'il définit FN. Ceci est prévu pour les définitions de fonction qui check-declarene reconnaît pas, par exemple, defstruct.

Notez qu'aux fins de check-declare, cette instruction doit être le premier espace non blanc sur une ligne.

Pour plus d'informations, voir Noeud Info (elisp)Declaring Functions.

A dessiné
la source
Un FILEONLYargument non nul est-il nécessaire pour le cas d'espèce? BTW, j'aurais donné la même réponse ;-).
Tobias
@Tobias: FILEONLYne semblait pas être nécessaire ici, pour moi. Ce qui semblerait indiquer que check-declarereconnaît le fet gdefuns.
Drew
@Drew, je pense que ce dernier commentaire fet gn'a de sens que dans le contexte d' emacs.stackexchange.com/q/39439 ?
phils
@phils: Oui, je voulais dire ceci: FILEONLYcela ne semblait pas être nécessaire ici, pour moi. Ce qui semble indiquer que check-declarereconnaît le increase-countdefun. ;-)
Drew
3

Je crois que le fait de placer la définition en question à l'intérieur eval-and-compilepermettrait également, superficiellement, d' obtenir le même résultat que dans la bonne réponse de Stefan :

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

Je connais cependant à peine les subtilités de l'utilisation eval-and-compileet, en outre, je ne m'attends pas à ce que cette approche soit en aucune façon supérieure.

Basilic
la source