Defun inside let com ligação lexical fornece aviso de compilação de bytes "a função não é conhecida por ser definida"

13

Eu quero obter o efeito de uma variável estática usando defundentro de letcom ligação lexical para criar um fechamento. No entanto, ao compilar o arquivo com bytes, recebo um aviso. Estou fazendo algo errado, ou se não, existe uma maneira de suprimir esse aviso?

Eu criei um 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))

O código funciona como esperado: a função increase-countexibe "Count is: n", em que n aumenta sempre que é chamado. No entanto, ao compilar este arquivo por byte, recebo o seguinte aviso:

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

Parece-me que increase-countsempre deve ser definido antes de ser chamado no final do bloco let. Não é este o caso?

Will Kunkel
fonte
defunnão faz o que você pensa que faz, sempre cria uma definição de nível superior.
Afinal de contas
2
Estou ciente de que ele cria uma definição de nível superior; isso é o que eu quero. Eu só quero que essa definição de nível superior seja um fechamento. Parece estar funcionando do jeito que eu quero, exceto por este aviso de compilação de bytes.
Will Kunkel

Respostas:

7

A maneira do compilador de bytes de decidir se uma função será definida ou não é muito "ingênua" e é enganada, mesmo no seu caso "óbvio". Mas você pode escrever de uma maneira que permita ao compilador entender o que acontece:

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

Obviamente, o melhor seria melhorar a lógica do compilador de bytes: os patches são bem-vindos para isso.

Stefan
fonte
5

Para suprimir o aviso do compilador de bytes, tente adicioná-lo antes do seu código, começando na coluna 0 (mais à esquerda):

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

C-h f declare-function diz lhe:

declare-functioné uma macro Lisp em subr.el.

(declare-function FN FILE &optional ARGLIST FILEONLY)

Diga ao compilador de bytes que a função FNestá definida, em FILE. O FILEargumento não é usado pelo byte-compiler, mas pelo check-declarepacote, que verifica se FILE contém uma definição FN.

FILEpode ser um arquivo Lisp (nesse caso, a ".el" extensão é opcional) ou um arquivo C. Os arquivos C são expandidos em relação ao "src/"diretório Emacs . Os arquivos Lisp são pesquisados locate-librarye, se isso falhar, eles são expandidos em relação ao local do arquivo que contém a declaração. A FILEcom um "ext:"prefixo é um arquivo externo. check-declareverificará esses arquivos se eles forem encontrados e os ignorará sem erro, se não forem.

Opcional ARGLISTespecifica FNos argumentos de, ou é tnão especificar FNos argumentos de. Um ARGLISTpadrão omitido para t, not nil: a nil ARGLISTespecifica uma lista de argumentos vazia e um explícito t ARGLISTé um espaço reservado que permite fornecer um argumento posterior.

Opcional FILEONLYnão nilsignifica que check-declareirá verificar única que FILEexiste, não que ele define FN. Isto é destinado a definições de funções que check-declarenão reconhecem, por exemplo defstruct,.

Observe que, para os fins de check-declare, essa declaração deve ser o primeiro espaço não em branco de uma linha.

Para mais informações, consulte Nó Informações (elisp)Declaring Functions.

Desenhou
fonte
Um FILEONLYargumento não nulo é necessário para o caso em questão? Entre, eu teria dado a mesma resposta ;-).
Tobias
@ Tobias: FILEONLYnão parecia ser necessário aqui, para mim. O que parece indicar que check-declarereconhece fe gdesaprova.
Tirou
@ Drew, acho que esse último comentário fe gsó faz sentido no contexto de emacs.stackexchange.com/q/39439 ?
19418 phils
@ phils: Sim, eu queria dizer o seguinte: FILEONLYnão parecia necessário aqui, para mim. O que parece indicar que check-declarereconhece o increase-countdesfecho. ;-)
Drew
3

Acredito que colocar a definição em questão eval-and-compiletambém atingiria superficialmente o mesmo resultado da resposta correta de Stefan :

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

No entanto, mal conheço as sutilezas do uso eval-and-compilee, além disso, não espero que essa abordagem seja de alguma forma superior.

Manjericão
fonte