10
$\begingroup$

Recently I noticed a peculiar behavior of MatrixForm that I failed to find a way to explain. Consider the following example:

a = {{1, 4}, {6, 9}} // MatrixForm;
a

My question is, after executing the code above, what is displayed in the notebook?

"That's simple! It's MatrixForm[{{1, 4}, {6, 9}}], notice you can't use this directly in evaluation!" Yeah, I thought so, until I tried:

enter image description here

"…OK, I know how to explain this. There's a number of functions whose output in the notebook is not the same as the one in the kernel, for example, Format:

Format[int[f_, x_]] := HoldForm@Integrate[f, x]
int[Sin[x], x]

enter image description here

"and MatrixForm is just another of them. " Good point, unless MatrixForm doesn't behave as follows:

enter image description here

So the output of MatrixForm in the notebook seems to exist as a combination of multiple states, it

  1. becomes a List if you edit and execute it;
  2. becomes a MatrixForm expression if you edit and press Ctrl+Shift+i;
  3. remains in a superposition of List and MatrixForm if you don't edit and just press Ctrl+Shift+i and Ctrl+Shift+n alternately.

Another function that behaves like this is NumberForm:

NumberForm[Pi // N, 10]

enter image description here

There may be more.

Further check shows this behavior has been existing at least since v3.

How to understand this behavior? A feature, or a long standing bug?

If it's a feature, is it possible to reproduce this behavior with self-made function?


Mr. Wizard thinks this post is a duplicate of this post, but that post is really about another issue. Let's look at the example in that post:

  mat = {{1, 2}, {3,4}};
  mat // MatrixForm
  Head[%]

enter image description here

I've been aware of this behavior of MatrixForm long before I discovered the one mentioned in this question, but I don't find it unexpected at all, because as we can see, in this case the output displays as

Out[5] // MatrixForm

rather than a single

Out[5]

which suggests that, the expression stored in Out[5] is exactly a List, rather than a MatrixForm expression. So when we use % (which is short for Out[]), there's no doubt we'll obtain a List.

In a word, that question is about how expression wrapped by MatrixForm is stored in the Out variable, while mine is about how expression wrapped by MatrixForm exists in the front end, they're different, though the answer to my question turns out to be related to $OutputForms, too.

$\endgroup$
  • $\begingroup$ Related: (44189), (132740) $\endgroup$ – Mr.Wizard Jun 26 '18 at 14:22
  • 1
    $\begingroup$ @Mr.Wizard I'm sorry, but that post is really about another issue, please check my update. As to Kuba's post, though Itai thinks my post is essentially a duplicate of that one, the answers to these 2 questions seem to be different at the moment, I think it's better to discuss further for a while and decide if they're duplicate later. $\endgroup$ – xzczd Jun 26 '18 at 14:41
  • 1
    $\begingroup$ I reopened and converted my links to "Related" instead. $\endgroup$ – Mr.Wizard Jun 26 '18 at 14:47
4
$\begingroup$

This is essentially a duplicate of this question:

  1. When typesetting as output, MatrixForm[_?ArrayQ] will display matrix-like.
  2. In an output cell, Convert To StandardForm will default to box formatting, so you go back and forth between a matrix-like state and an InputForm-like state.
  3. Once you edit the cell becomes an Input cell, so now MatrixForm will use an input-form like state for Convert To StandardForm.

So what's going on with that TagBox, and how does ToExpression@ToBoxes@... produce a list? Well, normally the second argument of a TagBox, in the absence of more specific rules, is applied as the head of the expression generated from the first argument:

MakeExpression[TagBox["a", b], StandardForm] 
(* HoldComplete[b[a]] *)

However, an exception is made for heads that are members of $OutputForms, where it is ignored:

$OutputForms // Unprotect
AppendTo[$OutputForms, b]
MakeExpression[TagBox["a", b], StandardForm]
(* HoldComplete[a] *)

This is "wrapper stripping" (the behavior of functions that "affect formatting, but not evaluation") is implemented. But it would be bad if wrapper stripping occured when you did a Convert To *Form. Fortunately, that operation doesn't use MakeExpresion/MakeBoxes, but rather a dedicated function, which knows not ignore those TagBoxes. Here is the call to convert from StandardForm to InputForm:

BoxForm`ConvertForm[TagBox["a", b], StandardForm, InputForm]
(* "b[a]" *)

To be perfectly honest, the only reason I can explain this behavior is that I came across the source code for it while fixing a bug, and was able to reproduce the reason for it from the commit message. It is a bit of black magic, but given the relatively small number of questions about it for the last 24 years or so, it is good enough to get the job done.

$\endgroup$
  • $\begingroup$ Oh, finally here comes a WRI member! So, Convert To StandardForm works differently in Input Cell and Output Cell? Is this behavior controlled by certain option? $\endgroup$ – xzczd Jun 26 '18 at 6:34
  • 1
    $\begingroup$ Would you consider posting an answer to (44189)? It would be illuminating in that context to know how the wrapper stripping is actually performed. $\endgroup$ – Mr.Wizard Jun 26 '18 at 14:32
5
$\begingroup$

Here are few notes:

Notice the second argument of underying TagBox[gridbox, Function[e, MatrixForm[e]]. It guides the interpretation from standard to input form (Ctrl+Shift+I). Since MatrixForm won't be forgotten, route back to boxes works too.

It is not documented but it seems to work like ToExpression[gridbox, StandardForm, Function[...]]. It won't work manually, ToExpression@ToBoxes@MatrixForm will return an array. Whether that is expected I don't know but I think this should be more consistent.

Here is another example (bug) showing the difference between Cell/ConvertTo items and composition of ToBoxes/Expression:

TagBox + InterpretationBox vs ConvertTo/StandardForm

Summing up:

  • Ctrl+Shift+I behavior is governed by TagBox (fine but undocumented)

  • ability to evaluate typeset MatrixForm comes from the fact that ToExpression strips it before parsing/evaluation. (arguable)

$\endgroup$
  • $\begingroup$ @xzczd Yes, fixed. But it does not matter so much as it is only a guess anyway ;) $\endgroup$ – Kuba Jun 18 '18 at 13:12
  • $\begingroup$ "ToExpression@ToBoxes@MatrixForm will return an array" I think this example is improper, because the information about MatrixForm is removed by ToBoxes in this case. (ToExpression does ignore MatrixForm though: TagBox[RowBox[{"(", "\[NoBreak]", GridBox[{{"1", "4"}, {"6", "9"}}], "\[NoBreak]", ")"}], Function[BoxForm`e$, MatrixForm[BoxForm`e$]]] // ToExpression) $\endgroup$ – xzczd Jun 18 '18 at 13:45
  • $\begingroup$ @xzczd did you mean, it is so but it should not? I agree something is off, maybe it is worth reporting. $\endgroup$ – Kuba Jun 18 '18 at 13:52
  • $\begingroup$ I'm not sure I should say As you've mentioned, the 2nd argument of TagBox is undocumented anyway. Perhaps it's time to report to WRI. (I was expecting some WRI member will come and answer this question. ) $\endgroup$ – xzczd Jun 18 '18 at 14:18
  • $\begingroup$ Oh… actually I've made a mistake in my second to last comment… I didn't notice MatrixForm[a] displays as a and thought the MatrixForm is ignored by ToBoxes $\endgroup$ – xzczd Jun 26 '18 at 6:10

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.