How does MakeBoxes handle an n-ary operator?

You were almost there. You just need to use the multiple-argument pattern, and generalize your code accordingly to create the internals of RowBox programmatically:

xO /: MakeBoxes[xO[x___], form_] :=
  RowBox[
    Riffle[
      Map[MakeBoxes[#, form] &, {x}],
      "\[Union]"
    ]
  ]

Note however that the above implementation leaks evaluation. It may or may not be a problem, but for example here:

x = 1;
xO[x == 1, x > 1, x < 1]

one may argue that the desired result should not be sensitive to the possible global values that x may have, so the result:

(* True \[Union] False \[Union] False *)

may be unsatisfacory. Thus, here is a more careful version:

ClearAll[xO];
SetAttributes[xO, HoldAllComplete]; 
xO /: MakeBoxes[xO[x___], form_] :=
  RowBox@Riffle[
     List @@ Replace[
       HoldComplete[x],
       elem_ :> With[{eval = MakeBoxes[elem, form]}, eval /; True],
       {1}
     ],
     "\[Union]"
  ]

which now gives

xO[x == 1, x > 1, x < 1]

(* x == 1 \[Union] x > 1 \[Union] x < 1 *)

You could make use of the internal typesetting function BoxForm`MakeInfixForm for this purpose:

xO /: MakeBoxes[xO[a__], form_] := RowBox[BoxForm`MakeInfixForm[Or[a], "\[Union]", form]]

The key feature is that parenthesization of the arguments is controlled by the precedence of the head of the first argument, in this case Or. Then:

xO[x == 1, x > 1, x < 1]
x == 1 \[Union] x > 1 \[Union] x < 1

If you want parenthesization of the arguments to be based on the precedence of Times instead of Or, you would do:

xO /: MakeBoxes[xO[a__], form_] := RowBox[BoxForm`MakeInfixForm[Times[a], "\[Union]", form]]

xO[x == 1, x > 1, x < 1]
(x == 1) \[Union] (x > 1) \[Union] (x < 1)

Tags:

Formatting