Defun inside let con enlace léxico da una advertencia de compilación de bytes "no se sabe que la función esté definida"

13

Quiero obtener el efecto de una variable estática usando defundentro de un letenlace léxico para crear un cierre. Sin embargo, al compilar byte el archivo, recibo una advertencia. ¿Estoy haciendo algo mal, o si no, hay alguna forma de suprimir esta advertencia?

He creado 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))

El código funciona como se esperaba: la función increase-countimprime "Count is: n" donde n aumenta cada vez que se llama. Sin embargo, al compilar byte este archivo, aparece la siguiente advertencia:

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

Me parece que increase-countsiempre debe definirse antes de que se llame al final del let-block. ¿No es este el caso?

Will Kunkel
fuente
defunno hace lo que crees que hace, siempre crea una definición de nivel superior. Elisp es después de todo no Esquema ...
wasamasa
2
Soy consciente de que crea una definición de nivel superior; eso es lo que yo quiero. Solo quiero que esa definición de nivel superior sea un cierre. Parece estar funcionando de la manera que quiero, excepto por esta advertencia de compilación de bytes.
Will Kunkel

Respuestas:

7

La forma del compilador de bytes para decidir si una función se definirá o no es muy "ingenua" y se deja engañar incluso en su caso "obvio". Pero puede escribirlo de una manera que permita al compilador comprender lo que sucede:

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

Por supuesto, aún mejor sería mejorar la lógica del compilador de bytes: los parches son bienvenidos para eso.

Stefan
fuente
5

Para suprimir la advertencia del compilador de bytes, intente agregar esto antes de su código, comenzando en la columna 0 (más a la izquierda):

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

C-h f declare-function Te dijo:

declare-functiones una macro Lisp en subr.el.

(declare-function FN FILE &optional ARGLIST FILEONLY)

Dile al compilador de bytes que la función FNestá definida, en FILE. El FILEcompilador de bytes no utiliza el argumento, sino el check-declarepaquete, que comprueba que ARCHIVO contiene una definición para FN.

FILEpuede ser un archivo Lisp (en cuyo caso la ".el" extensión es opcional) o un archivo C. Los archivos C se expanden en relación con el "src/"directorio Emacs . Los archivos de Lisp se buscan utilizando locate-library, y si eso falla, se expanden en relación con la ubicación del archivo que contiene la declaración. A FILEcon un "ext:"prefijo es un archivo externo. check-declarecomprobará dichos archivos si se encuentran y los omitirá sin error si no lo están.

Opcional ARGLISTespecifica FNlos argumentos de, o es tno especificar FNlos argumentos de. Un valor ARGLISTpredeterminado omitido es t, no nil: a nil ARGLISTespecifica una lista de argumentos vacía y un valor explícitot ARGLIST es un marcador de posición que permite suministrar un posterior.

Opcional FILEONLYno nilsignifica que check-declareverificará solo lo que FILEexiste, no lo que define FN. Esto está destinado a definiciones de funciones que check-declareno reconoce, por ejemplo,defstruct .

Tenga en cuenta que a los fines de check-declare , esta declaración debe ser el primer espacio no en blanco en una línea.

Para obtener más información, vea el nodo Información (elisp)Declaring Functions.

Dibujó
fuente
¿Es necesario un FILEONLYargumento no nulo para el caso en cuestión? Por cierto, habría dado la misma respuesta ;-).
Tobias
@Tobias: FILEONLYno parecía ser necesario aquí, para mí. Lo cual parece indicar que check-declarereconoce fy gdefunde.
Drew
@Drew, creo que ese último comentario fy gsolo tiene sentido en el contexto de emacs.stackexchange.com/q/39439 ?
Phil
@phils: Sí, quería decir esto: FILEONLYno parecía ser necesario aquí, para mí. Lo cual parecería indicar que check-declarereconoce el increase-countdefun. ;-)
Drew
3

Creo que colocar la definición en cuestión eval-and-compiletambién lograría superficialmente el mismo resultado que en la respuesta correcta de Stefan :

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

Sin embargo, apenas estoy familiarizado con las sutilezas del uso eval-and-compiley, además, no espero que este enfoque sea de ninguna manera superior.

Albahaca
fuente