Estou escrevendo uma função que, em princípio, recebe um número arbitrário de argumentos. Na prática, no entanto, só deve ser passado um número par de argumentos e, caso contrário, produzirá resultados indesejáveis.
Aqui está um exemplo fictício para o contexto:
(defun my-caller (&rest args)
(while args
(call-other-function (pop args) (pop args))))
Quando um arquivo elisp é compilado em bytes, o compilador de bytes lança um aviso quando vê uma função sendo chamada com o número errado de argumentos. Obviamente, isso nunca vai acontecer my-caller
, pois está definido para aceitar qualquer número.
Ainda assim, talvez haja uma propriedade de símbolo que eu possa definir ou um (declare)
formulário que eu possa adicionar à sua definição. Algo para notificar o usuário de que essa função deve receber apenas um número par de argumentos.
- Existe uma maneira de informar o compilador de bytes sobre essa restrição?
- Caso contrário, é possível com uma macro, em vez de uma função?
elisp
byte-compilation
Malabarba
fonte
fonte
Respostas:
EDIT : Uma maneira melhor de fazer isso no Emacs recente é definindo uma macro do compilador para verificar o número de argumentos. Minha resposta original usando uma macro normal é preservada abaixo, mas uma macro de compilador é superior porque não impede a passagem da função para
funcall
ouapply
em tempo de execução.Nas versões recentes do Emacs, você pode fazer isso definindo uma macro de compilador para sua função que verifica o número de argumentos e produz um aviso (ou até mesmo um erro) se não corresponder. A única sutileza é que a macro do compilador deve retornar o formulário de chamada de função original inalterado para avaliação ou compilação. Isso é feito usando um
&whole
argumento e retornando seu valor. Isso poderia ser realizado assim:Note-se que
funcall
eapply
agora pode ser usado, mas eles Ignorar verificação argumento pela macro compilador. Apesar do seu nome, macros compilador também parecem ser expandida no curso de 'interpretada' avaliação via C-xC-e, M-xeval-buffer, assim que você vai obter erros na avaliação, bem como sobre a compilação neste exemplo.A resposta original segue:
Aqui está como você pode implementar a sugestão de Jordon de "usar uma macro que fornecerá avisos no momento da expansão". Acontece que é muito fácil:
A tentativa de compilar o acima em um arquivo falhará (nenhum
.elc
arquivo é produzido), com uma boa mensagem de erro clicável no log de compilação, informando:Você também pode substituir
(error …)
por(byte-compile-warn …)
para produzir um aviso em vez de um erro, permitindo que a compilação continue. (Obrigado a Jordon por apontar isso nos comentários).Como as macros são expandidas no momento da compilação, não há penalidade no tempo de execução associada a essa verificação. Obviamente, você não pode impedir que outras pessoas liguem
my-caller--function
diretamente, mas pelo menos pode anunciar como uma função "privada" usando a convenção de hífen duplo.Uma desvantagem notável de usar uma macro para esse fim é que ela
my-caller
não é mais uma função de primeira classe: você não pode passá-la parafuncall
ouapply
em tempo de execução (ou pelo menos ela não fará o que você espera). Nesse aspecto, essa solução não é tão boa quanto poder simplesmente declarar um aviso do compilador para uma função real. Obviamente, o usoapply
tornaria impossível verificar o número de argumentos que estão sendo passados para a função em tempo de compilação, portanto, talvez essa seja uma troca aceitável.fonte
byte-compile-warn
apply
oufuncall
no invólucro da macro. Vou testá-lo e editar minha resposta, se funcionar.Sim, você pode usar
byte-defop-compiler
para realmente especificar uma função que compila sua função,byte-defop-compiler
possui alguns detalhes internos para ajudá-lo a especificar que suas funções devem gerar avisos com base em vários argumentos.Documentação
Adicione um formulário do compilador para FUNCTION.Se a função for um símbolo, a variável "byte-SYMBOL" deve nomear o código de operação a ser usado. Se a função for uma lista, o primeiro elemento é a função e o segundo elemento é o símbolo do bytecode. O segundo elemento pode ser nulo, significando que não há código de operação. COMPILE-HANDLER é a função a ser usada para compilar esse byte-op ou pode ser a abreviatura 0, 1, 2, 3, 0-1 ou 1-2. Se for nulo, o manipulador é "byte-compile-SYMBOL.
Uso
No seu caso específico, você pode usar uma das abreviações para definir que sua função receba dois argumentos.
Agora sua função emitirá avisos quando compilada com qualquer coisa, exceto 2 args.
Se você deseja dar avisos mais específicos e escrever suas próprias funções de compilador. Veja
byte-compile-one-arg
e outras funções semelhantes no bytecomp.el para referência.Observe que você não está apenas especificando alguma função para lidar com a validação, mas também a compilação. Novamente, as funções de compilação no bytecomp.el fornecerão uma boa referência.
Rotas mais seguras
Isso não é algo que eu tenha visto documentado ou discutido on-line, mas no geral eu diria que esse é um caminho inadequado. A rota correta (IMO) seria escrever seus defun com assinaturas descritivas ou usar uma macro que fornecerá avisos no momento da expansão, verificando o comprimento de seus argumentos e usando
byte-compile-warn
ouerror
para mostrar erros. Também pode ser útileval-when-compile
fazer a verificação de erros.Você também precisará que sua função seja definida antes de ser usada, e a chamada
byte-defop-compiler
precisará ser feita antes que o compilador atenda às chamadas reais de sua função.Novamente, parece não estar realmente documentado ou aconselhado pelo que vi (pode estar errado), mas imagino que o padrão a seguir aqui seja especificar algum tipo de arquivo de cabeçalho para o seu pacote que esteja cheio de desonestos vazios e liga para
byte-defop-compiler
. Basicamente, este seria um pacote necessário para que seu pacote real possa ser compilado.Opinião: Com base no que sei, o que não é muito, porque acabei de aprender sobre tudo isso, aconselho você a nunca fazer nada disso. sempre
fonte