# Prevent graphics from rendering inside a held expression

Assuming each odd line is input I would like the even lines to be the output of the following lines.

In the above the Graphics are inside Hold/HoldForm therefore IMO it doesn't make sense to have Mathematica try and render such expressions. For example Hold[Graphics[{Red, Circle[]}]] throws an error because built in colors don't get replaced.

How might I prevent Graphics from being rendered and instead print the Graphics code?

The following code works at reassigning Graphics inside Hold/HoldForm, but it prints something like Graphics[{Circle[{0, 0}]}] without the HoldForm/Hold.

Unprotect[Graphics]
Graphics /: HoldForm[Graphics[x___]] := (
InputForm[Graphics[x]]
);
Graphics /: Hold[Graphics[x___]] := (
InputForm[Graphics[x]]
);


Ideally the code should be generalized to work with irregular constructs like HoldForm["a",Graphics[{Circle[{0, 0}]}]]. More importantly, how might you insure HoldComplete doesn't render Graphics either. As rm-rf pointed out the Villegas–Gayley trick will likely be needed.

Simplifying John Fultz's answer slightly, you get the following:

Map[
(
Unprotect[#];
# /: MakeBoxes[#[expr_], fmt : StandardForm | TraditionalForm] :=
Block[{Graphics, Graphics3D},
RowBox[{ToString[#], "[", MakeBoxes[expr, fmt], "]"}]
];
Protect[#]
) &, {Hold, HoldForm, HoldComplete}
]

• This is not exactly what you want but: ToString[#, InputForm] &@Graphics[{Red, Circle[]}] – Kuba Sep 14 '13 at 0:32
• Also not exactly what you want, use graphics instead of Graphics and replace when it's time to render. – ssch Sep 14 '13 at 0:39
• I don't think you're going to get anywhere by monkeying with Graphics. As I see it, solving your problem requires changing the way the fonte end's output printer works. I don't have a clue on to do that. – m_goldberg Sep 14 '13 at 1:50
• Possibly related: Making customized InputForm and ShortInputForm. – Alexey Popkov Sep 15 '13 at 1:36
• Also closely related: "Conversion of expressions by the FrontEnd." – Alexey Popkov Jan 2 '14 at 18:22

John Fultz alluded to using the Villegas-Gayley pattern. Since I believe that is the correct approach to this problem here is an implementation.

mk : MakeBoxes[(Hold | HoldForm | HoldComplete | HoldPattern)[__], _] :=
Block[{$hldGfx = True, Graphics, Graphics3D}, mk] /; ! TrueQ[$hldGfx]


I included HoldPattern to complete the Hold functions. This now works at any depth:

Hold[1, 2, foo[3, Graphics[{Green, Circle[]}], 4], 5]

Hold[1, 2, foo[3, Graphics[{Green, Circle[]}], 4], 5]


Jacob Akkerboom questioned the overhead of this general rule attached to MakeBoxes. To test this I converted a large expression to Box form using ToBoxes (which calls MakeBoxes), and timed the operation with and without this definition as well as several alternatives. Here are the results (in version 7). Each test was performed in a fresh kernel, using this code:

expr = Expand[(1 + x + y)^4 (2 - z)^5 (q - 7 - a)^7 (b + r - 4)^6];
ToBoxes[expr] // AbsoluteTiming // First

• Raw (no additional MakeBoxes rules): 0.7940454
• Alternatives DownValue on MakeBoxes: 1.1030631
• Four individual DownValues on MakeBoxes: 1.4690841
• Four UpValues on Hold* functions: 0.7890451

(Incidentally, use of the Notations Package causes far larger overhead; the timing performed in my standard configuration was 3.5652039 seconds.)

It appears that Jacob's concern is valid as the overhead of my method above is significant, though not extreme. I usually attach rules to MakeBoxes to avoid having to Unprotect system Symbols but I may have to reconsider that practice. If you prefer unprotecting system Symbols to the overhead you may use this:

(
Unprotect @ #;
mk : MakeBoxes[Blank[#], _] /; ! TrueQ[$hldGfx] ^:= Block[{$hldGfx = True, Graphics, Graphics3D}, mk];
Protect @ #
) & ~Scan~ {Hold, HoldForm, HoldComplete, HoldPattern}

• Thank you! Now there are 2 good answers. I don't love the idea of changing the right answer after so long, but this is definitely the right/better/more complete answer. – William Jan 1 '14 at 22:55
• @Liam You're welcome, and thanks for the Accept. – Mr.Wizard Jan 2 '14 at 2:50
• @Liam I simplified my code; please take a look and let me know if you run into any problems. – Mr.Wizard Jan 2 '14 at 6:21
• @Mr.Wizard I suppose that this definition will slow down MakeBoxes quite a bit. When you call MakeBoxes[whatever[b]], I imagine it will at least have to test something like Hold === whatever, HoldForm === whatever etc. I think the definition will be associated with MakeBoxes. Also I suspect MakeBoxes is used once whenever you type a character in the front end. What are your thoughts on this? – Jacob Akkerboom Jan 2 '14 at 10:15
• @Jacob please see update – Mr.Wizard Jan 2 '14 at 17:51

Recall that the rendering of Graphics has nothing to do with evaluation. It is done entirely in typesetting. And therefore, a robust solution will treat this as a problem of typesetting, and not as a problem of evaluation.

Once you frame the problem properly, the solution is fairly straightforward. What you want to do is to change the typesetting of Hold (and friends). Take a look at this:

Unprotect[Hold];
Hold /: MakeBoxes[Hold[expr_], fmt : StandardForm | TraditionalForm] :=
Block[{Graphics, Graphics3D}, Unprotect[Graphics, Graphics3D];
Clear[Graphics, Graphics3D];
RowBox[{"Hold", "[", MakeBoxes[expr, fmt], "]"}]]
Protect[Hold]


Fortunately, Hold (and HoldForm and HoldComplete) has no typesetting rules directly attached to it that you're fighting, which you can determine using FormatValues[Hold]. But Graphics and Graphics3D do; it's how typesetting of graphics works at all. We want to suppress those rules, but only within the typesetting of Hold. So we use Block to contain the damage we're about to do to the Graphics and Graphics3D symbols, and then use Clear to clear them. From there on out, we let MakeBoxes do what it would normally do.

Note that this example cheats a bit; it only works if you pass one argument to Hold. I did that for purpose of code simplicity and illustration. To make the formatting rules work properly for Hold[expr___], I would have to write multiple and more sophisticated rules, or I would have to use the Villegas-Gayley trick.

Edit: As came up in the comment discussion, it really isn't necessary to Unprotect and Clear the symbols Graphics and Graphics3D, as Block is effectively doing that already. I've considered editing the code to make it shorter/simpler, but perhaps the existing code is clearer for people who don't fully understand how Block works (and, public confession here, while I understand Block scoping, I had just plumb forgotten how Block initializes variables, so this more an oversight on my part than a planned teaching moment).

• Nice explanation and solution,+1. It is not clear to me however, why Unprotect and Clear are needed, since Block automatically clears all symbol's definitions and attributes (in contrast to InternalInheritedBlock). – Leonid Shifrin Sep 14 '13 at 13:34
• @LeonidShifrin I think it was simply John Fultz kind attempt to make it clearer to what is exactly going on. They way it is currently written makes perfect sense(at least to me). Additionally if I don't understand the differences between With,Module, and Block/InternalInheritedBlock. How do you prevent evaluation of a function? Well by Removing and Clearing it of course. – William Sep 14 '13 at 14:55
• Actually, believe it or not, there are limits to my knowledge of Mathematica, and for some reason, it didn't occur to me to see if Block would automatically clear a Protected System symbol. I appreciate @Liam reading the benefit of hidden wisdom into my answer, though! :) And, in fact, that response gives me pause about editing the answer to shorten the code. – John Fultz Sep 14 '13 at 16:36
• In reply to your last comment: Yes, you are correct. I was hoping to clear any confusion when I was comparing AtomQ[Graph[...]] with AtomQ[Unevaluated[Graph[...]]], where the former gives True and the latter gives False. Unevaluated is not responsible for the difference :) – Jacob Akkerboom Jan 2 '14 at 18:43
• @AlexeyPopkov in reply to your first comment: ah yes this is precisely the point :). If you "Set" gr = Graph[...], then the expression with head Graph is evaluated outside the Block. Note that blocking Graph later will have no effect as the expression with head Graph is already converted into an atomic expression. Even if it's Head is still Graph, it is not a "proper head". Anyway the function Graph generates this atomic expression. If you evaluate gr inside the Block, then the expression with head Graph is not converted into an atom, as Graph has no special meaning. – Jacob Akkerboom Jan 2 '14 at 18:48

This is not an answer but an extended comment.

This occurs because although the front end attempts to render the Graphics element the internal code won't replace Directives inside of a Held expression.

This is not the case. Consider

Hold[Graphics[{RGBColor[1, 0, 0], Thick, Circle[]}]]


and

 With[{red = Red}, Hold[Graphics[{red, Circle[]}]]]


So the error message comes from using the built-in symbol Red and not from the front end doing anything funny with directives.

• Is Color Directives more appropriate then? Seems to be an issue for all colors. – William Sep 14 '13 at 1:59
• @Liam. RGBColor[1, 0, 0] is a color directive; Red,Green, Blue, etc. are not. – m_goldberg Sep 14 '13 at 2:02
• That's fair although I find it odd that the colors are listed under guide/GraphicsDirectives Graphics Directives in the docs. Would simply colors be an appropriate name? – William Sep 14 '13 at 2:04
• @Liam. I regard that to be an error in the docs. They are symbols that evaluate to graphic directives, but of course not inside Hold. – m_goldberg Sep 14 '13 at 2:08
• @Liam. You might well ask "Why then does Thick work?" I wonder about that myself. – m_goldberg Sep 14 '13 at 2:12

If you take @JohnFultz 's initial comment

Recall that the rendering of Graphics has nothing to do with evaluation. It is done entirely in typesetting. And therefore, a robust solution will treat this as a problem of typesetting, and not as a problem of evaluation.

then I would ask, why are you using Hold in the first place? Hold is for the kernel, not for the front-end. I tried your Hold[Graphics[{Red, Circle[]}]] and saw the error too; but look at the expression inside the notebook (Cmd-Shift-E on Mac)

Cell[BoxData[
RowBox[{"Hold", "[",
GraphicsBox[{Red, CircleBox[{0, 0}]}], "]"}]], "Output",
CellChangeTimes->{3.597606336110736*^9}]


The front-end wraps all in a Cell (content in a notebook lives inside cells). Cell contains boxes, and boxes are generated from the expression. It also replaced Graphics by GraphicsBox and Circle by CircleBox. Red is unknown to the front-end as it is.

Try

Graphics[{Red, Circle[]}] // InputForm


and you see

Graphics[{RGBColor[1, 0, 0], Circle[{0, 0}]}]


Funny enough, it seems the kernel knows about Red and Circle's default and the front-end gets those symbols evaluated. Graphics gets passed as is, although -I bet- it must have some rules in the kernel that allow the front-end to get a clearer command.

If you do

Hold[Graphics[{Red, Circle[]}]] // InputForm


you see in the front-end

Hold[Graphics[{Red, Circle[]}]]


The kernel didn't attempt to resolve the expression at all - as expected.

I am far from being an expert on these things, but I think the central issue is trying to use Hold in the front-end, which as we can see, has no effect. The front-end has its own transformation rules, from the expressions it gets from the kernel to the cell and box objects it needs to display.

The good -and confusing- thing is that the kernel can manipulate those symbols too (Cell, boxes, InputForm, etc), so one can return formatted expressions to the front-end and this is how (IMHO) the problem should be approached.

So a very simple approach is just to use InputForm as suggested above.

In M11+, if you are willing to create a new Hold* wrapper, you could define:

SetAttributes[HoldFormatting,HoldAll];

MakeBoxes[HoldFormatting[expr_], StandardForm]^:=MakeBoxes[DisableFormatting[expr]]


Then:

HoldFormatting[{1+1, Graphics[{Green,Circle[]}]}]


{1 + 1, Graphics[{Green, Circle[]}]}