4
$\begingroup$

I ask because I'm trying to make Times[a, z^-1] //TraditionalForm display as $a z^{-1}$ rather than $a/z$. If I write

MakeBoxes[Power[z, n_Integer], TraditionalForm] :=
  ToBoxes[Superscript[z,n], TraditionalForm];

then z^-1 //TraditionalForm displays as $z^{-1}$, as expected, but Times[a, z^-1] //TraditionalForm gives me $a/z$. If I add

MakeBoxes[head_[a__,Power[z, n_Integer]], TraditionalForm] :=
  ToBoxes[head[a, Superscript[z,n]], TraditionalForm];

then I get my desired result $a z^{-1}$, so it appears that MakeBoxes is applied at the top level only. Am I interpreting this correctly?

$\endgroup$
  • $\begingroup$ With your first code piece, you can use MapAll , i.e., with TraditionalForm//@ (a z^(-1)) formating reaches the Powers that lie deeper. For example: TraditionalForm //@ ( (a z^(-1) + w^(y^(-2)))^(-3) + x^(-5)) $\endgroup$ – kglr Jul 14 '17 at 18:29
3
$\begingroup$

No, you are not interpreting this correctly, at least not entirely.

MakeBoxes is always applied recursively, unless the formatting rule for an outer expression decideds hijack the process for an inner expression. If this didn't happen, how could {Red, Disk[]} display as a list of a red swatch and the word Disk[], but Graphics[{Red,Disk[]}] give you an actual red disk?

This is basically what you are running into here. In fact, your comment above about Times[a, Power[z,-1]] reducing to Divide[a,z] is essentially completely reversed. A symbolic fraction is a product of one or more Power expressions:

In[124]:= a/b //FullForm
Out[124]//FullForm= Times[a,Power[b,-1]]
In[125]:= a /( c b) //FullForm
Out[125]//FullForm= Times[a,Power[b,-1],Power[c,-1]]

For this reason, the formatting rule of Times explictly ignores the formatting rules of Power while it's formatting. Your second rule works because it is circurmvents Times formatting, and in fact it would work just as well for your purposes (and be faster) if you wrote it like this:

MakeBoxes[Times[a___, Power[z, n_Integer]], TraditionalForm] := ToBoxes[a Superscript[z, n], TraditionalForm];

Note that you risk possibly reordering terms in the product this way, but if you always want the z^-1 at the end you're probably OK. Of course, you also need the first rule for when z^-1 appears on its own, or inside a different function which doesn't hijack the recursion (which should be just about all functions).

About MakeBoxes more generally: MakeBoxes is a kernel function. The order of evaluation is

  1. The FE sends boxes to the kernel
  2. All steps described in "The Main Loop", culminating in complete evaluation of the input and applying $PrePrint.
  3. The kernel calls MakeBoxes on the result, and transmits the boxes to the FE to be inserted into the notebok.

The FE never "applies" any function, except in certain simple cases inside of Dynamic expressions and controls. If it doubt, it will transmit things to the kernel make sure they are computed properly, and the make use of the result.

$\endgroup$
  • $\begingroup$ This is very enlightening. I don't understand the phrase "...unless the formatting rule for an outer expression decideds hijack the process for an inner expression." Is there a rule for deciding when to hijack the process, or are these irregular heuristics? $\endgroup$ – QuantumDot Jul 15 '17 at 4:23
  • $\begingroup$ With very few execptions, the hijacking is obvious. Things like Graphics(3D), Graph, Image, obviously are not necessarily formatting the individual expressions inside of them via the normal rules of those expressions on their own. A less obvious example is SpanFromLeft on its own vs inside a Grid. Due to the vagaries of mathematical notation and our own fullform, there are special rules for +,-, *, /, ^, and their combinations. I'm sure there are a few others, though I can't think of any off of the top of my head. $\endgroup$ – Itai Seggev Jul 15 '17 at 4:50
  • 1
    $\begingroup$ I suppose it depends on your interpretation of the meaning of "applied recursively." The default formatting rules, for example for f[args___], will call MakeBoxes and thus look up all definitions for each of the arg_i. In that sense the problem in this case isn't that MakeBoxes isn't called recursively, it's that Times has very special formmating rules. It is true tail definitions MakeBoxes[...]:=... will not call MakeBoxes again unless explictly present on the RHS. So if you do MakeBoxes[foo,StandardForm]:=2+bar, you will get a pink box as output. $\endgroup$ – Itai Seggev Jul 15 '17 at 16:47
  • 1
    $\begingroup$ We need to distinguish between evaluation rules and formatting rules. Divide[a,b] for symbolic a and b always evaluates to a*b^-1. Now, MakeBoxes is HoldAllComplete and does not allow evaluation to occur. So what you are seeing in Carl's TracePrint is that the formatting rule for Times will in many cases rewrite an expression as Divide[num,denom and allow the formatting rules for Divide to fire. (A fact I had forgotten.) I may teak my answer to make this clearer... $\endgroup$ – Itai Seggev Jul 18 '17 at 17:34
  • 1
    $\begingroup$ Is there any documentation for all this special behavior? $\endgroup$ – Rodney Price Jul 18 '17 at 20:35
1
$\begingroup$

You can see what is going on by using TracePrint:

TracePrint[
    MakeBoxes[a z^-1,TraditionalForm],
    _MakeBoxes,
    TraceInternal->True,
    TraceAction->(Print[FullForm[#]]&)
]

HoldForm[MakeBoxes[Times[a,Power[z,-1]],TraditionalForm]]

HoldForm[MakeBoxes[Times[a,Power[z,-1]],TraditionalForm]]

HoldForm[MakeBoxes[Divide[a,z],TraditionalForm]]

HoldForm[MakeBoxes[a,TraditionalForm]]

HoldForm[MakeBoxes[z,TraditionalForm]]

FractionBox["a", "z"]

From the above, we can see that your initial DownValues for MakeBoxes never fires.

$\endgroup$
  • 1
    $\begingroup$ All right, so in the absence of the second MakeBoxes definition, Times[a,Power[z,-1]] reduces to Divide[a,z] before the first definition can fire. Put the second definition in place, and it fires before the reduction happens. Therefore MakeBoxes is applied recursively (in the front end, presumably). Does this mean that every time a reduction happens (in the kernel) that MakeBoxes matches against it? $\endgroup$ – Rodney Price Jul 14 '17 at 19:31
  • 2
    $\begingroup$ From the documentation: MakeBoxes is not automatically called on the results it generates. This means that explicit MakeBoxes calls must typically be inserted into definitions that are given. $\endgroup$ – Carl Woll Jul 14 '17 at 19:42
  • $\begingroup$ @CarlWoll that isn't relevant here, as he is calling ToBoxes on the RHS $\endgroup$ – Itai Seggev Jul 14 '17 at 23:31
  • $\begingroup$ @ItaiSeggev I'm not sure what you're objecting to here, but my TracePrint clearly shows that MakeBoxes[Power[z, -1], TraditionalForm] is never called, hence the OP format value never gets triggered. I don't know what that has to do with the use of ToBoxes on the RHS side of his MakeBoxes[Power[z, n_Integer], TraditionalForm] rule that never gets called. $\endgroup$ – Carl Woll Jul 15 '17 at 0:01
  • 1
    $\begingroup$ I was objecting to "From the documentation: MakeBoxes is not automatically called on the results it generates. This means that explicit MakeBoxes calls must typically be inserted into definitions that are give." That has no bearing on on either his rules, or even really to his suggestion about when MakeBoxes is called. $\endgroup$ – Itai Seggev Jul 15 '17 at 3:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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