Leaving aside WB (you really don't need to answer your question), the problem is a direct answer based only on how expressions are evaluated during assignments. Here is an example:
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
To make it work, I intentionally made a definition for notGoodQ , to always return True . Now, why was g[x0_] evaluated at the time of assignment via TagSetDelayed ? The answer is that although TagSetDelayed (as well as SetDelayed ) in the assignment h/:f[h[elem1,...,elemn]]:=... does not apply any rules that f may have, it will evaluate h[elem1,...,elem2] , as well as f . Here is an example:
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
The fact that TagSetDelayed is equal to HoldAll does not mean that it does not evaluate its arguments - it means that the arguments come to it are invaluable and regardless of whether they will be evaluated depends on the semantics of TagSetDelayed (which I briefly described above). The same is true for SetDelayed , so a commonly used statement that it "does not evaluate with its arguments" is not literally correct. A more correct statement is that he receives arguments invaluable and really evaluates them in a special way - do not evaluate rhs, but for lhs, evaluate the head and elements, but do not apply the rules for the head. To avoid this, you can wrap things in a HoldPattern , for example:
Clear[g,notGoodQ]; notGoodQ[x_]:=EvenQ[x]; g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[]) g/:cccQ[HoldPattern[g[x0_]]]:=True;
It continues. Here are some ways to use it:
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
Note, however, that the need for a HoldPattern inside your left side when defining a definition is often a sign that the expression inside your head may also be evaluated during a function call, which may violate your code. Here is an example of what I mean:
In[1532]:= ClearAll[f,h]; f[x_]:=x^2; f/:h[HoldPattern[f[y_]]]:=y^4;
This code tries to catch cases like h[f[something]] , but it will obviously fail because f[something] will evaluate before the evaluation comes to h :
In[1535]:= h[f[5]] Out[1535]= h[25]
For me I need a HoldPattern on lhs is a sign that I need to review my design.
EDIT
As for debugging during loading into WB, one thing can be done (IIRC, I can’t check right now) - use the good old print instructions, the output of which will be displayed on the WB console. Personally, I rarely feel the need for a debugger for this purpose (debugging a package at boot)
EDIT 2
In response to editing in the question:
As for the order of definitions: yes, you can do it, and it solves this specific problem. But, as a rule, this is not reliable, and I would not consider this a good general method. It is difficult to give concrete advice for the case in question, since it is slightly different from its context, but it seems to me that using UpValues is unjustified here. If this is done to handle errors, other ways to do this are without using UpValues .
As a rule, UpValues are most often used to safely overload a function without adding any rule to the overloaded function. One tip is to avoid tying UpValues to heads that also have DownValues , and can evaluate - while doing this, you start playing the game with the appraiser and eventually lose. The safest way is to attach UpValues to inert characters (heads, containers), which are often the “type” of objects onto which you want to overload a given function.
Regarding my comment on the presence of HoldPattern indicating a bad design. Of course, for HoldPattern , for example, this is (somewhat artificial), legal use:
In[25]:= Clear[ff,a,b,c]; ff[HoldPattern[Plus[x__]]]:={x}; ff[a+b+c] Out[27]= {a,b,c}
This is justified here, because in many cases Plus remains unappreciated and useful in its unappreciated form - since it can be deduced that it represents the amount. We need a HoldPattern here because the Plus path is defined by one argument, and because the template is the only argument during the definition (even if it describes several arguments as a whole). Thus, we use HoldPattern here to prevent the template from being treated as a regular argument, but this is basically different from the intended use cases for Plus . Whenever this happens (we are sure that the definition will work correctly for the intended use cases), HoldPattern in order. Note btw that this example is also fragile:
In[28]:= ff[Plus[a]] Out[28]= ff[a]
The reason that everything is still basically OK is that we usually do not use Plus for a single argument.
But there is a second group of cases where the structure of the usually supplied arguments is the same as the structure of the templates used for the definition. In this case, the evaluation of the template during the assignment indicates that the same evaluation will occur with the actual arguments during function calls. Your use falls into this category. My comment on the design flaw was for such cases - you can prevent the evaluation of the template, but you also have to interfere with the evaluation of the arguments to make this work. And matching patterns to an incompletely evaluated expression is fragile. In addition, the function should not accept additional conditions (besides the fact that it can check for type) for arguments.