Laissant de côté le WB (qui n'est pas vraiment nécessaire pour répondre à votre question) - le problème semble avoir une réponse simple basée uniquement sur la façon dont les expressions sont évaluées pendant les affectations. Voici un exemple :
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
Pour que cela fonctionne, j'ai délibérément créé une définition pour notGoodQ
pour toujours retourner True
. Maintenant, pourquoi était g[x0_]
évalué pendant l'affectation via TagSetDelayed
? La réponse est que, alors que TagSetDelayed
(ainsi que SetDelayed
) dans une affectation h/:f[h[elem1,...,elemn]]:=...
n'applique aucune règle qui f
peut avoir, il évaluera h[elem1,...,elem2]
, ainsi que f
. Voici un exemple :
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
Le fait que TagSetDelayed
est HoldAll
ne signifie pas qu'il n'évalue pas ses arguments - cela signifie seulement que les arguments lui arrivent non évalués, et qu'ils soient ou non évalués dépend de la sémantique de TagSetDelayed
(que j'ai décrit brièvement ci-dessus). Il en va de même pour SetDelayed
, de sorte que l'affirmation couramment utilisée selon laquelle il "n'évalue pas ses arguments" n'est pas littéralement correcte. Une déclaration plus correcte est qu'il reçoit les arguments non évalués et les évalue d'une manière spéciale - n'évalue pas les r.h.s., tandis que pour l.h.s., évalue la tête et les éléments mais n'applique pas de règles pour la tête. Pour éviter cela, vous pouvez envelopper les choses dans HoldPattern
, comme ceci :
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Cela passe. Voici quelques utilisations :
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
Notez cependant que le besoin de HoldPattern
à l'intérieur de votre côté gauche lorsque vous faites une définition est souvent un signe que l'expression à l'intérieur de votre tête peut également être évaluée lors de l'appel de la fonction, ce qui peut casser votre code. Voici un exemple de ce que je veux dire :
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Ce code tente d'attraper des cas comme h[f[something]]
, mais cela échouera évidemment puisque f[something]
évaluera avant que l'évaluation n'arrive à h
:
In[1535]:= h[f[5]]
Out[1535]= h[25]
Pour moi, le besoin de HoldPattern
sur la gauche est un signe que je dois reconsidérer ma conception.
MODIFIER
En ce qui concerne le débogage lors du chargement dans WB, une chose que vous pouvez faire (IIRC, ne peut pas vérifier pour le moment) est d'utiliser de bonnes vieilles instructions d'impression, dont la sortie apparaîtra dans la console de WB. Personnellement, je ressens rarement le besoin d'un débogueur à cet effet (débogage du paquet lors du chargement)
MODIFICATION 2
En réponse à la modification de la question :
Concernant l'ordre des définitions :oui, vous pouvez le faire, et cela résout ce problème particulier. Mais, généralement, ce n'est pas robuste, et je ne considérerais pas cela comme une bonne méthode générale. Il est difficile de donner un conseil précis pour un cas concret, car il est un peu hors de son contexte, mais il me semble que l'utilisation de UpValues
ici est injustifié. Si cela est fait pour la gestion des erreurs, il y a d'autres manières
pour le faire sans utiliser UpValues
.
Généralement, UpValues
sont utilisés le plus souvent pour surcharger certaines fonctions de manière sûre, sans ajouter de règle à la fonction surchargée. Un conseil est d'éviter d'associer UpValues
avec des têtes qui ont aussi DownValues
et peut évaluer - en faisant cela, vous commencez à jouer à un jeu avec évaluateur, et finirez par perdre. Le plus sûr est d'attacher UpValues
aux symboles inertes (têtes, conteneurs), qui représentent souvent un "type" d'objets sur lesquels on veut surcharger une fonction donnée.
Concernant mon commentaire sur la présence de HoldPattern
indiquant une mauvaise conception. Il y en a certainement il y en a utilisations légitimes de HoldPattern
, comme celle-ci (quelque peu artificielle) :
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Ici, c'est justifié car dans de nombreux cas Plus
reste non évalué, et est utile sous sa forme non évaluée - puisqu'on peut en déduire qu'il représente une somme. Nous avons besoin de HoldPattern
ici à cause de la façon dont Plus
est défini sur un seul argument, et parce qu'un motif se trouve être un seul argument (même s'il décrit généralement plusieurs arguments) lors de la définition. Donc, nous utilisons HoldPattern
ici pour éviter de traiter le modèle comme un argument normal, mais cela est principalement différent des cas d'utilisation prévus pour Plus
. Chaque fois que c'est le cas (nous sommes sûrs que la définition fonctionnera correctement pour les cas d'utilisation prévus), HoldPattern
c'est bien. Notez d'ailleurs que cet exemple est également fragile :
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
La raison pour laquelle c'est encore généralement correct est que normalement nous n'utilisons pas Plus
sur un seul argument.
Mais, il existe un deuxième groupe de cas, où la structure des arguments habituellement fournis est la même que la structure des motifs utilisés pour la définition. Dans ce cas, l'évaluation de modèle pendant l'affectation indique que la même évaluation se produira avec des arguments réels pendant les appels de fonction. Votre utilisation entre dans cette catégorie. Mon commentaire pour un défaut de conception était pour de tels cas - vous pouvez empêcher l'évaluation du modèle, mais vous devrez également empêcher les arguments d'évaluer, pour que cela fonctionne. Et le pattern-matching contre une expression non complètement évaluée est fragile. De plus, la fonction ne doit jamais supposer des conditions supplémentaires (au-delà de ce qu'elle peut vérifier par type) pour les arguments.