Deixando de lado o WB (que não é realmente necessário para responder à sua pergunta) - o problema parece ter uma resposta direta baseada apenas em como as expressões são avaliadas durante as atribuições. Aqui está um exemplo:
In[1505]:=
notGoodQ[x_]:=True;
Clear[g];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
In[1509]:= g/:cccQ[g[x0_]]:=True
During evaluation of In[1509]:= g::nogood: -- Message text not found -- (x0_)
Out[1509]= $Aborted
Para fazê-lo funcionar, eu deliberadamente fiz uma definição para
notGoodQ
para sempre retornar True
. Agora, por que foi g[x0_]
avaliado durante a atribuição através de TagSetDelayed
? A resposta é que, enquanto TagSetDelayed
(assim como SetDelayed
) em uma atribuição h/:f[h[elem1,...,elemn]]:=...
não aplica nenhuma regra que f
pode ter, ele avaliará h[elem1,...,elem2]
, bem como f
. Aqui está um exemplo:In[1513]:=
ClearAll[h,f];
h[___]:=Print["Evaluated"];
In[1515]:= h/:f[h[1,2]]:=3
During evaluation of In[1515]:= Evaluated
During evaluation of In[1515]:= TagSetDelayed::tagnf: Tag h not found in f[Null]. >>
Out[1515]= $Failed
O fato de
TagSetDelayed
é HoldAll
não significa que ele não avalie seus argumentos - significa apenas que os argumentos chegam a ele sem avaliação, e se eles serão avaliados ou não depende da semântica de TagSetDelayed
(que eu descrevi brevemente acima). O mesmo vale para SetDelayed
, portanto, a declaração comumente usada de que "não avalia seus argumentos" não está literalmente correta. Uma afirmação mais correta é que ela recebe os argumentos não avaliados e os avalia de uma maneira especial - não avalia o r.h.s, enquanto para l.h.s. avalia cabeça e elementos, mas não aplica regras para a cabeça. Para evitar isso, você pode envolver as coisas em HoldPattern
, assim:Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Isso passa. Aqui está algum uso:
In[1527]:= cccQ[g[1]]
Out[1527]= True
In[1528]:= cccQ[g[2]]
During evaluation of In[1528]:= g::nogood: -- Message text not found -- (2)
Out[1528]= $Aborted
Observe, no entanto, que a necessidade de
HoldPattern
dentro do seu lado esquerdo ao fazer uma definição geralmente é um sinal de que a expressão dentro de sua cabeça também pode ser avaliada durante a chamada de função, o que pode quebrar seu código. Aqui está um exemplo do que quero dizer:In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Este código tenta capturar casos como
h[f[something]]
, mas obviamente falhará, pois f[something]
avaliará antes que a avaliação chegue a h
:In[1535]:= h[f[5]]
Out[1535]= h[25]
Para mim, a necessidade de
HoldPattern
no l.h.s. é um sinal de que preciso reconsiderar meu design. EDITAR
Com relação à depuração durante o carregamento no WB, uma coisa que você pode fazer (IIRC, não pode verificar agora) é usar as boas e velhas instruções de impressão, cuja saída aparecerá no console do WB. Pessoalmente, raramente sinto necessidade de depurador para esse fim (pacote de depuração ao carregar)
EDITAR 2
Em resposta à edição na pergunta:
Em relação à ordem das definições:sim, você pode fazer isso e resolve esse problema específico. Mas, geralmente, isso não é robusto, e eu não o consideraria um bom método geral. É difícil dar um conselho definitivo para um caso em questão, pois está um pouco fora de seu contexto, mas me parece que o uso de
UpValues
aqui é injustificável. Se isso for feito para tratamento de erros, há outras maneiras
para fazer isso sem usar UpValues
. Geralmente,
UpValues
são usados mais comumente para sobrecarregar alguma função de forma segura, sem adicionar nenhuma regra à função que está sendo sobrecarregada. Um conselho é evitar associar UpValues
com cabeças que também têm DownValues
e pode avaliar - fazendo isso você começa a jogar um jogo com avaliador, e eventualmente perderá. O mais seguro é anexar UpValues
a símbolos inertes (cabeças, contêineres), que geralmente representam um "tipo" de objetos nos quais você deseja sobrecarregar uma determinada função. Em relação ao meu comentário sobre a presença de
HoldPattern
indicando um projeto ruim. Certamente há usos legítimos para HoldPattern
, como este (um pouco artificial):In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Aqui se justifica porque em muitos casos
Plus
permanece não avaliado, e é útil em sua forma não avaliada - pois pode-se deduzir que ele representa uma soma. Precisamos de HoldPattern
aqui por causa do modo Plus
é definido em um único argumento e porque um padrão é um único argumento (mesmo que descreva geralmente vários argumentos) durante a definição. Então, usamos HoldPattern
aqui para evitar tratar o padrão como argumento normal, mas isso é principalmente diferente dos casos de uso pretendidos para Plus
. Sempre que esse for o caso (temos certeza de que a definição funcionará corretamente para os casos de uso pretendidos), HoldPattern
está bem. Observe b.t.w., que este exemplo também é frágil:In[28]:= ff[Plus[a]]
Out[28]= ff[a]
A razão pela qual ainda está tudo bem é que normalmente não usamos
Plus
em um único argumento. Mas, há um segundo grupo de casos, onde a estrutura dos argumentos normalmente fornecidos é a mesma que a estrutura dos padrões usados para a definição. Nesse caso, a avaliação do padrão durante a atribuição indica que a mesma avaliação acontecerá com os argumentos reais durante as chamadas da função. Seu uso se enquadra nesta categoria. Meu comentário para uma falha de design foi para esses casos - você pode impedir que o padrão seja avaliado, mas terá que impedir que os argumentos sejam avaliados também, para que isso funcione. E a correspondência de padrões com expressões não completamente avaliadas é frágil. Além disso, a função nunca deve assumir algumas condições extras (além do que ela pode verificar) para os argumentos.