Why does MakeBoxes mess up RowBox ordering?

When I use RowBox for MakeBoxes the order of the elements is changed for TraditionalForm, if there is a plus (+) in the list of elements.

MakeBoxes[x, form_] = RowBox[{"b", "+", "a"}];
MakeBoxes[x, StandardForm]
(* Out[2] = RowBox[{"b", "+", "a"}]
Out[3] = RowBox[{"a", "+", "b"}] *)


Sometimes Mathematica really makes me wonder. Does this only happen when there is a plus in RowBox? Is there any way to fix this?

• FWIW, Block[{TraditionalFormDump$UseNewTraditionalForm = False},MakeBoxes[x, TraditionalForm]] returns RowBox[{"b", "+", "a"}]. – Leonid Shifrin Oct 7 '13 at 17:01 • FWIW^2, associating the definition to x, which I think it's a good idea, returns what you expect – Rojo Oct 7 '13 at 17:04 • @Leonid, you are using variables The Google does not know about, therefor they do not exist. :-) But you are right. Where can you read about$UseNewTraditionalForm? – Friedrich Oct 7 '13 at 17:27
• @Mr.Wizard I haven't tested, but probably attaching it to TraditionalForm, or even to MakeBoxes but explicitly as DownValues, may work – Rojo Oct 7 '13 at 22:04
• @Rojo thanks for correcting me – Mr.Wizard Oct 8 '13 at 1:51

When I see this right, then the evil function is TraditionalFormDumpordplus. This seems to change the order. The arguments can be extracted from a Trace

TraditionalForm[a + b]; (* Dummy call *)
TraditionalFormDumpordplus[{{"+", "b"}, {"+", "a"}}, {}]

(* {2,1} *)


If we change this to give a sorted list, then your arguments are not reordered

ClearAll[TraditionalFormDumpordplus]
TraditionalFormDumpordplus[l1_, _] := Range[Length[l1]]

MakeBoxes[x, form_] = RowBox[{"b", "+", "a"}];
MakeBoxes[x, StandardForm]

(*
RowBox[{"b", "+", "a"}]
RowBox[{"b", "+", "a"}]
*)

• Impressive! How did you find out about TraditionalFormDumpordplus? A trace does not give me this function. Do you have any idea why this does not happen when I associate the definition to x as Rojo mentioned? – Friedrich Oct 7 '13 at 18:07
• @Friedrich I believe it does not happen because definitions associated with x are tried before those associated with TraditionalForm (just like with UpValues), and this special bothersome definition is attached to TraditionalForm's FormatValues. Values associated with MakeBoxes (such as your definitions) are tried last. – Rojo Oct 7 '13 at 20:41
• @Friedrich You have to use TraceInternal->True as option to Trace. – halirutan Oct 7 '13 at 21:20

halirutan gave a very nice explanation why this only happens in TraditionalForm and not in StandardForm. For completeness I will add the workaround that Rojo mentioned: Associate the definition to x and not to MakeBoxes using UpSet:

MakeBoxes[x, form_] ^= RowBox[{"b", "+", "a"}];


Association with TraditionalForm also works:

Unprotect[TraditionalForm];
TraditionalForm /: MakeBoxes[x, TraditionalForm] = RowBox[{"b", "+", "a"}];


You can also interfere with the sorting done by TraditionalForm by wrapping your boxes in TagBox, InterpretationBox or TemplateBox:

MakeBoxes[x, form_] = TagBox[RowBox[{"b", "+", "a"}], #&];
MakeBoxes[x, StandardForm]


TagBox[RowBox[{"b", "+", "a"}], #1 &]

TagBox[RowBox[{"b", "+", "a"}], #1 &]

(I would actually use a TagSet here, but for pedagogical reasons I avoided it.) This avoids the need to modify an internal Mathematica symbol, as in the accepted answer (there may also be good examples where the reordering is beneficial, as in multivariate polynomials). Also, as Mr.Wizard points out, the other answers using TagSet only work when x is at the top-level. For example:

y /: MakeBoxes[y, form_] = RowBox[{"b", "+", "a"}];

With the TagBox approach, the non-top level example still works:
MakeBoxes[{x}, TraditionalForm]