Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have some code that looks like

Table[
    a = 1;
    b = {2, 3}
    c = i;,
    {i, 2}
]

which gives an error:

Set::write: Tag Times in c {2,3} is Protected. >>

In this case, it is quite clear that there is a semicolon missing after b = {2, 3}, which is causing this error. However, sometimes I encounter this in large code blocks spanning several lines, which is very difficult to debug.

How can I automate this semicolon hunting to make debugging easier?

share|improve this question
3  
Worth to mention that Tag Times ... is Protected message is in 90% cases the result of a missing semicolon. – Kuba Feb 3 at 7:02
    
Please, upvote the comment of Kuba! – Rexcirus Mar 1 at 20:36
up vote 26 down vote accepted

Here is a function findBadSets that will find any explicitly bad Set/SetDelayed attempts in a given expression. Simply wrap it around a syntactically complete block of code, or follow the block with // findBadSets and the errors are printed one per row, protected symbol followed by complete left-hand side for each bad Set:

(* your example *) // findBadSets

Code for the function:

SetAttributes[findBadSets, HoldFirst]

findBadSets[expr_] :=
  Cases[
    Unevaluated @ expr,
    (Set | SetDelayed)[bad : head_Symbol[___], _]
      /; MemberQ[Attributes@head, Protected] :>
        HoldForm[Row[{head, bad}, Spacer[50]]],
   -1] // Column

See also:

share|improve this answer
    
WoW, so useful! – matheorem Nov 15 '13 at 1:32
    
@matheorem My pleasure. :-) – Mr.Wizard Nov 15 '13 at 19:03
    
That's certainly a faq. – Kuba Feb 3 at 6:44
    
@Kuba thanks for tagging. – Mr.Wizard Feb 3 at 6:54
    
This is a perfect example of what we could get from code helping functions. For some time now that WRI talks about the eventual inclusion of code improvement suggestion tools. A typical example given is the suggestion of WL functions that could do exactly what the user is programming with a more complex code. But even simpler things can be included, like the small code of Mr. Wizard, but also, for instance, a warning check of "all" the things listed on our common pitfalls post. Is there a frontend framework missing for this purpose? I would even be willing to participate/$ on a crowdfunding... – P. Fonseca Feb 3 at 8:46

One can use Trace for this. I stuck in an extra, different error to show it is omitted in the output. It should also be clear how to trace other error messages.

foo = Trace[
  Table[
    a = 1/0;
    b = {2, 3} 
    c = i;,
   {i, 2}],
  HoldPattern@Message[Set::write, ___],
  TraceAbove -> True]
(* Power::infty, Set::write errors *)

Mathematica graphics

If you would like a function like Mr.Wizard's to apply to a code block, we can wrap it up like this:

traceSet = Function[code,
  Trace[
   code,
   HoldPattern@Message[Set::write, __],
   TraceAbove -> True],
  HoldAll]

Here is a way to view the list of lists of lists of output, somewhat reminiscent of WReach's traceView. (His function is built on TraceScan, which I could not get to work in the way above. The option TraceAbove -> True seems to be ignored. Is that a bug?)

Clear[ov];
ov[{e1_HoldForm, rest___}, open_: False] := 
  OpenerView[{e1, Column[ov[#, open] & /@ {rest}, Dividers -> All]}, open];
ov[e_, _] := e

Example:

ov[foo, True]

Mathematica graphics

Now you can see the offending code comes from

a = 1/0; b = {2, 3} c = i;

which leads to the actual error, {2, 3} c = i. Note that the error message already informs us that the immediate source is c {2,3}.

If it's a very large chunk of code, you can select the bottom segment of the evaluation chain as follows. The length of the chain is controlled by

Length@First@pos == 5

which can be adjusted to suit:

Cases[foo, 
  e_List /; 
   With[{pos = Position[e, HoldPattern@Message[Set::write, ___]]},
    Length[pos] == 1 && Length@First@pos == 5], Infinity] // 
 Column[#, Dividers -> All] &

Mathematica graphics

One could also apply the OpenerView to each element in the output by adding to the pattern in the second argument of Cases the code :> ov[e, True] to make it a transformation rule.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.