Undesirable evaluation in assignments in Mathematica: why this happens and how to debug it during package loading?

I am developing a (large) package that no longer downloads. This happened after I changed one line of code. When I try to download a package (using Needs), the package starts downloading, and then one of the setdelayed definitions comes to life (that is, it is somehow evaluated), it falls into the error trap loaded a few lines earlier, and the package loads interrupts .
The interrupt error capture procedure does its job, except that it should not have been called first during the package loading phase. The error message shows that the wrong argument is actually a template expression that I use in lhs from the set of the given definition a few lines later.

Something like that:

……Some code lines Changed line of code g[x_?NotGoodQ]:=(Message[g::nogood, x];Abort[]) ……..some other code lines g/: cccQ[g[x0_]]:=True 

When I try to download a package, I get:

 g::nogood: Argument x0_ is not good 

As you can see, the past argument is a template, and it can only come from a line of code.

I tried to find the reason for this behavior, but so far I have not been successful. So I decided to use the powerful debugging tools of Workbench.

I would like to see step by step (or with breakpoints) what happens when a package loads. I'm not too familiar with WB yet, but it seems that using Debug as ... the package is loaded first, and then eventually debugged with breakpoints, etc. My problem is that the package does not even load completely! And any breakpoint set before loading the package does not seem effective.

So ... 2 questions:

  • Can anyone explain why these lines of code come to life during package loading? (there are apparently no obvious syntax errors or code snippets in the package)
  • can someone explain how (if) it is possible to check / debug package code when loading into WB?

Thanks for any help.

Edit

In light of Leonid’s answer and the use of his EvenQ example: We can avoid using Holdpattern simply by defining upvalues ​​for g BEFORE the lower values ​​for g

 notGoodQ[x_] := EvenQ[x]; Clear[g]; g /: cccQ[g[x0_]] := True g[x_?notGoodQ] := (Message[g::nogood, x]; Abort[]) 

Now

 ?g Global`g cccQ[g[x0_]]^:=True g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[]) In[6]:= cccQ[g[1]] Out[6]= True 

but

 In[7]:= cccQ[g[2]] During evaluation of In[7]:= g::nogood: -- Message text not found -- (2) Out[7]= $Aborted 

So ... the general rule:

When writing the g function, first define the up values ​​for g, then define the downvalues ​​for g, otherwise use Holdpattern

Can you subscribe to this rule?

Leonid says that using Holdpattern may indicate an improved design. Besides the solution mentioned above, how can you improve the design of small code above or, better, generally when working with upvalues?

thanks for the help

+8
wolfram-mathematica workbench
source share
1 answer

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.

+12
source share

All Articles