Seguindo o código de exemplo mínimo do Common Lisp (de uma base de código muito mais complexa), recebo avisos sobre line ((fboundp (car ',arg)) ,arg)
e não consigo explicar por que ou como evitá-los.
(defmacro cond-issue (&key (arg '(a b c)))
(let ((arg-var (gensym "arg-var-")))
`(let ((,arg-var (cond ((not (listp ',arg)) (list 'arg))
((fboundp (car ',arg)) ,arg)
(t ',arg))))
`(apply #'+ ,,arg-var))))
(cond-issue :arg (e f g))
CCL reclama:
;Compiler warnings :
; In an anonymous lambda form: Undefined function E
; In an anonymous lambda form: Undeclared free variable F
; In an anonymous lambda form: Undeclared free variable G
SBCL reclama:
; in: cond-issue :arg
; (E F G)
;
; caught style-warning:
; undefined function: common-lisp-user::e
;
; caught warning:
; undefined variable: common-lisp-user::f
;
; caught warning:
; undefined variable: common-lisp-user::g
;
; compilation unit finished
; Undefined function:
; e
; Undefined variables:
; f g
; caught 2 WARNING conditions
; caught 1 STYLE-WARNING condition
CLISP não reclama.
Com o SBCL tentei as duas coisas: compilar e avaliar o código, sem diferença quanto aos avisos. [editar: aprendi com as respostas que fiz isso errado]
Este seria um exemplo de caso de uso (bobo) para o segundo cond
formulário.
(cond-issue :arg (car '(e f g)))
;; ⇒ (apply #'+ e)
Qual a explicação, por que a CCL e a SBCL estão reclamando?
E como posso escrever o código para satisfazer CCL e SBCL?
O propósito desta macro não está muito claro para mim, mas acho que a questão sobre a natureza desses avisos pode ser abordada. Adicionei algumas notas sobre um possível redesenho no final da resposta.
A razão pela qual SBCL e CCL estão reclamando é que estão compilando código. A resposta CLISP interpreta o código. Se você abrir uma nova replicação SBCL e colocá-la
eval
no modo interpretado, a macro OP poderá ser invocada sem disparar avisos:Depois de
eval
voltar ao modo compilado, os avisos aparecem:Observando a expansão da macro acima, pode-se observar que um ramo do código a ser compilado contém uma chamada de função com uma função indefinida e duas variáveis indefinidas:
((FBOUNDP (CAR '(E F G))) (E F G))
. O compilador está reclamando porque não consegue compilar este código. Eu acho que esse é o comportamento que você realmente deseja, ou seja, você provavelmente não deveria tentar contornar os avisos do compilador para funções e variáveis indefinidas.Considere algum código que deve ser compilado:
Aqui o formulário
(LIST 1 2)
substituiu o anterior(E F G)
e isso não representa nenhum problema para o compilador. Observe que o formulário resultante acima é(APPLY #'+ (1 2))
e este não é um código lisp legal. Acho que a intenção era gerar(APPLY #'+ '(1 2))
em vez disso. Além disso, a invocação(cond-issue :arg 42)
produz resultados(APPLY #'+ (ARG))
onde suspeito que(APPLY #'+ '(42))
era desejado. Aqui está uma versão ligeiramente modificada quecond-issue
corrige esses problemas:Novamente, acho que você não deveria tentar contornar esses avisos do compilador, mas como um experimento, e no espírito de consertar uma solução incompleta, você poderia agrupar uma
cond-issue
macro que verifica os argumentos primeiro:Esta solução tem algumas ressalvas:
boundp
efboundp
verifica ligações no ambiente global, mas não pode inspecionar ligações locais (e não tenho certeza se existe uma maneira simples de fazer isso). Além disso, o wrapper rejeitará invocações de macro como acontece(cond-issue-check :arg 42)
com um erro. Ocond-issue-check
wrapper poderia ser aumentado para validar melhor a entrada, mas a definição atual deve ser suficiente para deixar claro. Agora seria um bom momento para questionar o propósito da macro, se ela precisa ser uma macro, e para revisitar o design.No exemplo final acima, o wrapper de verificação não pode determinar se as ligações locais para
a
eb
estão vinculadas. O original, desembrulhado,cond-issue
pode ser chamado quando ligações locais são necessárias, mas isso significa que avisos podem ser acionados para funções ou variáveis não vinculadas.De qualquer forma, a correção acima não interromperá todos os avisos de definições ausentes. Em geral, eu não recomendaria tentar suprimir esses tipos de avisos e não recomendaria usar
cond-issue-check
. Provavelmente é melhor deixar o compilador fazer o seu trabalho.Redesenhando a Macro
Parece que o objetivo do OP é tomar como entrada formulários que descrevam outros formulários aos quais
#'+
podem ser aplicados e, a partir disso, criar formulários que não precisam ser válidos em seu ambiente atual. Se for esse o caso, aqui está um design alternativo.Aqui, se o primeiro elemento de um formulário de entrada for um símbolo fbound,
#'+
será gerado um formulário que se aplica ao resultado da chamada do formulário de entrada. Caso contrário, será gerado um formulário que se aplica#'+
a todos os argumentos.Alguns exemplos de invocações:
Como mencionado anteriormente,
fboundp
inspeciona ligações globais, mas não consegue dizer se as variáveis lexicais estão vinculadas a funções, ou seja, aquelas criadas comflet
oulabels
. Aqui estão exemplos que mostram o uso de uma função global para a qualcond-something
gera a saída esperada e o uso de uma função local para a qualcond-something
não geraria o código que se poderia ingenuamente esperar:Adição:
CLISP também reclama:
Agora vamos compilar a função `TEST:
Você então precisa olhar para a macroexpansão:
Então veja a resposta por ad absurdum ...