# The clearest way to represent Mathematica's evaluation sequence

WReach has presented here a nice way to represent the Mathematica's evaluation sequence using OpenerView. It is much more clear way to go than using the standard Trace or TracePrint commands. But it could be improved further.

I need straightforward way to represent the real sequence of (sub)evaluations inside Mathematica's main loop for beginners. In particular, it should be obvious when new evaluation subsequence begins and from which expression (it is better to have each subsequence exactly in one Opener). The evaluation (sub)sequence should be identified as easily as possible with the standard evaluation sequence. I mean that the reader should be able to map real evaluation step to one described in the Documentation for the standard evaluation sequence.

Is it possible?

The cited OpenerView solution used Trace / TraceOriginal to generate its content. This allowed the definition of show in that response to be defined succinctly, but had the disadvantage of discarding some of the trace information. TraceScan provides more information since it calls a user-specified function at the start and end of every evaluation.

Two functions are defined below that try to format the TraceScan information in (somewhat) readable form.

traceView2 shows each expression as it is evaluated, along with the subevaluations ("steps") that lead to the result of that evaluation. "Drill-down" is provided by OpenerView. The function generates output that looks like this:

traceView2[(a + 1) + 2]


As one drills deeper into the view, it rapidly crawls off the right-hand side of the page. traceView4 provides an alternative view that does not exhibit the crawling behaviour at the expense of showing much less context for any given evaluation:

The definitions of the functions follow...

## traceView2

ClearAll@traceView2
traceView2[expr_] :=
Module[{steps = {}, stack = {}, pre, post, show, dynamic},
pre[e_] := (stack = {steps, stack}; steps = {})
; post[e_, r_] :=
( steps = First@stack ~Join~ {show[e, HoldForm[r], steps]}
; stack = stack[[2]]
)
; SetAttributes[post, HoldAllComplete]
; show[e_, r_, steps_] :=
Grid[
steps /. {
{} -> {{"Expr  ", Row[{e, " ", Style["inert", {Italic, Small}]}]}}
, _ -> { {"Expr  ", e}
, {"Steps", steps /.
{ {} -> Style["no definitions apply", Italic]
, _ :> OpenerView[{Length@steps, dynamic@Column[steps]}]}
}
, {"Result", r}
}
}
, Alignment -> Left
, Frame -> All
, Background -> {{LightCyan}, None}
]
; TraceScan[pre, expr, ___, post]
; Deploy @ Pane[steps[[1]] /. dynamic -> Dynamic, ImageSize -> 10000]
]
SetAttributes[traceView2, {HoldAllComplete}]


## traceView4

ClearAll@traceView4
traceView4[expr_] :=
Module[{steps = {}, stack = {}, pre, post},
pre[e_] := (stack = {steps, stack}; steps = {})
; post[e_, r_] :=
( steps = First@stack ~Join~ {{e, steps, HoldForm[r]}}
; stack = stack[[2]]
)
; SetAttributes[post, HoldAllComplete]
; TraceScan[pre, expr, ___, post]
; DynamicModule[{focus, show, substep, enter, exit}
, focus = steps
; substep[{e_, {}, _}, _] := {Null, e, Style["inert", {Italic, Small}]}
; substep[{e_, _, r_}, p_] :=
{ Button[Style["show", Small], enter[p]]
, e
, Style[Row[{"-> ", r}], Small]
}
; enter[{p_}] := PrependTo[focus, focus[[1, 2, p]]]
; exit[] := focus = Drop[focus, 1]
; show[{e_, s_, r_}] :=
Column[
{ Grid[
{ {"Expression", Column@Reverse@focus[[All, 1]]}
, { Column[
{ "Steps"
, focus /.
{ {_} :> Sequence[]
, _ :> Button["Back", exit[], ImageSize -> Automatic]
}
}
]
, Grid[MapIndexed[substep, s], Alignment -> Left]
}
, {"Result", Column@focus[[All, 3]]}
}
, Alignment -> Left, Frame -> All, Background -> {{LightCyan}}
]
}
]
; Dynamic @ show @ focus[[1]]
]
]
SetAttributes[traceView4, {HoldAllComplete}]

• Very nice! Now the sequence of evaluations is really visually clear! One thing that Trace* commands totally loose is applying the Orderless attribute. I was amazed detecting that Plus[a, 1] in really is evaluated again in the form Plus[1, a] after applying the Orderless attribute! It seems to contradict the standard evaluation sequence description. Apr 3, 2011 at 7:39
• Nifty. Now code something similar for XML... (if you have nothing better to do) :) +1. Apr 3, 2011 at 10:27
• Could you please explain why you only apply Dynamic at the end in traceView2, by substituting every dynamic with Dynamic? For performance reasons? Mar 9, 2012 at 1:47
• @IstvánZachar The dynamic substitution is unnecessary -- it is a leftover piece of code from a previous version of the function. It could even be considered harmful as it is preferable to defer the creation of the inner OpenerViews. I'd fix it, but I notice that these functions now perform very poorly for complex traces in versions of Mathematica released since I wrote this response. It seems that some relevant performance characteristic changed in a later microrevision of V7 (possibly V7.0.1). These functions need to be revisited (but I cannot do that right at the moment). Mar 10, 2012 at 18:36
• Why don't you put this code on GitHub? Right now people may be copying this code from here, and modifying it for their personal use. But the improvements never make it back here for the whole community's benefit. GitHub would encourage people to either contribute the changes back, or at least would make it easier to track down modified version. Why GitHub and not another site? Because GitHub allows people to edit the source in a browser without even needing to install git. This can actually work with short functions like this. Mar 5, 2013 at 18:29

Just an update on WReach's extremely useful traceView function: more compact view, larger buttons for opening/collapsing hierarchy, saves button-states as well.

ClearAll[traceViewCompact];
SetAttributes[traceViewCompact, {HoldAllComplete}];
traceViewCompact[expr_] :=
Module[{steps = {}, stack = {}, pre, post, show, default = False},
pre[e_] := (stack = {steps, stack}; steps = {});
post[e_,
r_] := (steps = First@stack~Join~{show[e, HoldForm@r, steps]};
stack = stack[[2]]);
SetAttributes[post, HoldAllComplete];
show[e_, r_, steps_] := Module[{open = False},
Grid[
steps /. {{} -> {{"Expr  ",
Item[e, Background -> GrayLevel@.8]}}, _ -> {{"Expr  ",
e}, {Toggler[
Dynamic@
open, {True ->
Button["Steps", Appearance -> {"DialogBox", "Pressed"}],
False -> Button@"Steps"}],
steps /. {{} -> Style["no definitions apply", Italic], _ :>
Dynamic@
If[open, Column@steps,
Grid@{{Length@steps, "steps"}}]}}, {"Result", r}}},
Alignment -> {Left, Center}, Frame -> All,
Spacings -> Automatic, Background -> {{Hue[.65, .1, 1]}, None}]
];
TraceScan[pre, expr, ___, post];
Deploy@Column@{
Opener@Dynamic@default,
Dynamic@Pane[First@steps, ImageSize -> 10000]
}];

traceViewCompact[(a + 1) + 2]


• In v10.3, it is easily get frozen and break down mma. You can try to trace this function mathematica.stackexchange.com/a/80173/4742 Oct 29, 2015 at 1:05
• To be honest, I've never really used these tracers, not even mine. Does the original traceView of WReach (above) work fine in v10.3? If so, I recommend using that, as I sadly don't have the time to debug debuggers. Could be an awfully recursive problem : ) Oct 29, 2015 at 9:09
• Ok, I understand. WReach's function works fine in 10.3 Oct 29, 2015 at 10:18

Here's my approach, also based on WReach's OpenerView technique. Its layout is much more compact, though less explicit, than his traceView2, and as far as I can tell, the only information sacrificed is the display of the number of steps hidden inside each OpenerView. Expressions that are unchanged by evaluation are indicated by a disabled OpenerView, though the screenshot that I made doesn't show a difference between a disabled and enabled OpenerView.

SetAttributes[TraceView, HoldFirst]

TraceView[e_, s___, opts : OptionsPattern[Trace]] :=
Module[{show2},
show2[{expr_, steps__}] :=
OpenerView[{expr, Column[show2 /@ {steps}]}];
show2[{HoldForm[x_]}] := Row[{Opener[True, Enabled -> False], HoldForm[x]}];
show2[HoldForm[x_]] := HoldForm[x];
show2[Trace[Unevaluated[e], s, opts, TraceOriginal -> True]]
]


• It is interesting to note that outputs of TraceScan and Trace with TraceOriginal -> True slightly differs. In the second case evaluation chain for (a+1) is shown with additional step "a+1" before step "1+a". I do not understand what this additional step does represent. Have anybody any ideas? Apr 6, 2011 at 5:23
• I have answered this question in separate thread. Mar 20, 2012 at 8:07

Here is another implementation of joebolte's TraceView using the new Tree functionality in 12.3:

SetAttributes[TraceView, HoldFirst];
TraceView[expr_] := Module[{trace, f, h, tree},
trace = Trace[expr, TraceOriginal -> True];
f[steps_List] := Rest[steps];
f[step_] := {};
h[steps_List] := First[steps];
h[step_] := step;
tree = NestTree[f, trace, Infinity, h];
TreeOutline[tree]]