# Make a custom object look like MatrixForm of a matrix?

Let's say I have a custom object that I denote

Obj[a,b,c,...]


There are always n^2 entries in the object for some integer n, and I'd like it to appear as a matrix once it is entered. For example, I'd like to get the following output:

Obj[x11,x12,x21,x22]


and

Obj[x11,x12,x13,x21,x22,x23,x31,x32,x33]


And of course it should also just give the other way around:

Obj[x11,x12,x21,x22]

I guess the biggest challenge is to make it work for any n automatically. Is it possible to do that in Mathematica?

InterpretationBox will take care of making safe round trip from boxes to expression but we need to take extra care during condition checking and partition not to evaluate Obj's arguments.

It does not matter that Obj is not holding them, Obj itself can be held. Hold @ Obj[1,2,3,4] etc.

### Edit to the old code

As xzczd has noticed, an Input cell with e.g. Obj[1,2,3,4] shows explicit MatrixForm after Ctrl+Shit+N.

Which is strange, the more that I can't reproduce that with combinations of NotebookRead/MakeExpression/MakeBoxes etc.

The problem seems to be caused by TagBox so we can take even more extra care and work around it:

Obj /: MakeBoxes[
o : Obj[args__], StandardForm
] /; IntegerQ @ Sqrt @ Length @ Unevaluated @ args := With[
{ array = (
List @@@ Partition[Hold[args], Sqrt@Length[Unevaluated[args]]]
) /. Hold[lists__] :> Map[
Function[x, MakeBoxes[x, StandardForm], HoldFirst]
, Unevaluated @ {lists}
, {2}
]
}
, InterpretationBox[
RowBox[{ "(", "\[NoBreak]"
, GridBox[array, RowSpacings -> 1, ColumnSpacings -> 1
, RowAlignments -> Baseline, ColumnAlignments -> Center
]
, "\[NoBreak]", ")"
}]
, o
]
]


### Old

Obj /: MakeBoxes[
o : Obj[args__], StandardForm
] /; IntegerQ @ Sqrt @ Length[Unevaluated[args]] := With[
{ matrixFormBoxes = (
List @@@ Partition[Hold[args], Sqrt @ Length[Unevaluated[args]]]
) /. Hold[lists__] :> MakeBoxes[MatrixForm[{lists}]]
}
, InterpretationBox[
matrixFormBoxes
, o
]
]


Obj[1, 2, 3, 4]
Obj[1, 2, 4]
Hold @ Obj[Echo[1], 2, 3, 4, 5, 6, 7, 8, 9]


% // ReleaseHold


% // FullForm

Obj[1,2,3,4,5,6,7,8,9]


related:

https://mathematica.stackexchange.com/a/149668/5478

• One issue remained: if one writes e.g. Obj[1, 2, 3, 4] in an input cell, and press Ctrl+Shift+N, then the code will display as MatrixForm[{{x11, x12, x13}, {x21, x22, x23}, {x31, x32, x33}}]. Nov 6, 2017 at 14:35
• Good workaround. I just found another possible workaround, not as flexible as yours, but simpler: modify MakeBoxes[MatrixForm[{lists}]] to MakeBoxes[{lists}, TraditionalForm]. Nov 7, 2017 at 4:26
• As to evaluation leaks, box formatting functions should, in my opinion, always use "strong" HoldAllComplete-type holding, to avoid nasty things like: Hold[___, nasty, ___] ^:= Print@"Leak!"; HoldComplete@Obj[1, 2, nasty, 3]. Nov 7, 2017 at 13:11
• @Kuba Just use HoldComplete instead of Hold and HoldAllComplete instead of HoldFirst. Or did you mean the issue mentioned by xzczd? Then I don't have anything better than what is already in your answer. Nov 7, 2017 at 15:00
• @Kuba Ctrl+Shift+N calls BoxFormConvertForm[BoxData[RowBox[{"Obj", "[", RowBox[{"1", ",", "2", ",", "3", ",", "4"}], "]"}]], StandardForm, StandardForm, "Input"]. It seems that MakeBoxes called by BoxFormConvertForm somehow ignores standard formatting of builtin ...Form wrappers and converts MatrixForm[...] to RowBox[{"MatrixForm", "[", ..., "]"}] instead of ordinary GridBox. I don't know how it happens. Nov 7, 2017 at 17:09

InterpretationBox used in answer by J.M. and by Kuba allows you to copy formatted object and keep its proper interpretation as an Obj expression, but you can't edit such formatted objects. You could make InterpretationBox editable by using Editable -> True option but this will allow you to just edit formatted version while interpretation would incorrectly remain unchanged.

To make copied formatted object editable, instead of InterpretationBox, you could use TemplateBox with appropriate DisplayFunction:

Obj // ClearAll
Obj /: MakeBoxes[Obj@args__, StandardForm] := Module[{n, k},
n = Length@Unevaluated@args;
k = Sqrt@n;
TemplateBox[
MakeBoxes /@ Unevaluated@{args},
"Obj",
Tooltip -> "Obj",
DisplayFunction -> (Evaluate@RowBox@{"(", "\[NoBreak]", GridBox[
Partition[Array[Slot, n], k],
RowSpacings -> 1, ColumnSpacings -> 1,
RowAlignments -> Baseline, ColumnAlignments -> Center
], "\[NoBreak]", ")"}&)
] /; IntegerQ@k
]


Example of copying and editing of formatted object:

Obj[1, 2, 3, 4]


• Instead of including TooltipBox in the DisplayFunction, you could add the option Tooltip->"Obj" to the TemplateBox. Nov 7, 2017 at 15:44
• @CarlWoll thanks, changed. I had it in DisplayFunction because originally tooltip was RowBox@{"Obj", "[", RowBox@Riffle[Array[Slot, n], ","], "]"}, but it didn't update after editing formatted expression, so I changed it to a "constant" tooltip. Nov 7, 2017 at 15:55
• This is neat. There are pieces of this I can use on some stuff I'm working on. Thank you! Nov 7, 2017 at 21:20

Obj /: MakeBoxes[Obj[args__], form : StandardForm] :=
With[{arr = Partition[{args}, Sqrt[Length[{args}]]]},
With[{boxes = MakeBoxes[MatrixForm[arr], form]},
InterpretationBox[boxes, Obj[args]]]]


I make no attempt to check if the number of arguments is a square number; you can add that check yourself, if you wish.

• This will leak evaluation. e.g. Hold@Obj[Echo[1], 2, 3, 4]
– Kuba
Nov 6, 2017 at 14:24
• @Kuba what does that mean? Is it bad? EDIT: Removed the accept for now, to make sure I selected the best answer. Nov 6, 2017 at 14:26
• @Kagaratsch, yes, this is why you shouldn't accept answers too quickly. ;) If you're concerned about leaks, then Kuba's approach is certainly better than mine. Nov 6, 2017 at 15:06

Here is my solution:

Format[Obj[x__]] := MatrixForm[Partition[{x}, Sqrt[Length[{x}]]]]


It seems short and nice, but the solution by J.M. or Kuba is better for the following reason. If we enter an object, then copy-and-paste the output and try FullForm, on my objects above we get:

while the objects of J.M. or Kuba properly give:

I wonder why Format is not implemented a bit more sophisticated? As a naive end-consumer I'd expect it to take the right hand side as a pattern, not directly use it, but go into the box structure and make necessary adjustments automatically to make sure that under any circumstances where the output is copied or transformed to input form, the correct initial input is recovered.