# Passing an argument with the alignment operator (&) to a macro within another aligned environment

I am trying to write macros for quickly formatting matrices in math mode. Here is a simple, working example:

\documentclass{article}
\usepackage{amsmath}
\newcommand{\pmat}[1]{\begin{pmatrix}#1\end{pmatrix}}

\begin{document}
\begin{displaymath}
M = \pmat{1&0\\0&1}
\end{displaymath}
\end{document}


This also works if used within an aligned environment (a table, align environment, cases environment, etc.):

\documentclass{article}
\usepackage{amsmath}
\newcommand{\pmat}[1]{\begin{pmatrix}#1\end{pmatrix}}

\begin{document}
\begin{align}
M &= \pmat{1&0\\0&1}
\end{align}
\end{document}


Normally, within the align environment the & symbol tells LaTeX to start a new column, but since the matrix values are contained between {} group tokens, LaTeX does not attempt to parse the 1&0\\0&1 argument and thus everything works as it was intended.

Let's create a new macro that does not use the group tokens {} to denote the macro argument. For example, it is fairly common to use square brackets to group an argument as in this working example (try it!):

\documentclass{article}
\usepackage{amsmath}
\newcommand{\bmat}[1][]{\begin{bmatrix}#1\end{bmatrix}}

\begin{document}
\begin{displaymath}
M = \bmat[1&0\\0&1]
\end{displaymath}
\end{document}


So far, so good. Here comes the problem! If I try to use my new macro \bmat within another aligned environment:

% DOES NOT WORK!!!
\documentclass{article}
\usepackage{amsmath}
\newcommand{\bmat}[1][]{\begin{bmatrix}#1\end{bmatrix}}

\begin{document}
\begin{align}
M &= \bmat[1&0\\0&1]
\end{align}
\end{document}


I get an error because LaTeX tries to interpret the & within the matrix argument as part of the outer align environment.

The difference between the way LaTeX treats the first macro \pmat{} and the second one \bmat[] is that the group tokens {} prevent LaTeX from parsing the argument contained within the curly braces until after the macro \pmat has been called. On the other hand, when LaTeX sees the \bmat[arg] macro, the argument is not surrounded by group tokens and so LaTeX tries to parse it before getting eaten up by \bmat. The extra & character screws up the syntax of the align environment and so LaTeX crashes. At least, that is my understanding of what is going on, and I could be wrong!

So, the question is whether or not it is possible to force LaTeX to treat the argument in the [] case just as it did in the {} case (without causing additional problems). In other words, I want to be able to write a macro a-la \bmat[1&0\\0&1] that works in aligned environments but retains the square bracket syntax.

CONCLUSION:

Here is the final macro with working [] syntax, thanks to egreg (requires xparse and must be contained in a separate package file or between \makeatletter and \makeatother in preamble):

\DeclareDocumentCommand\mat{}{{\ifnum\z@=}\fi\@mat}
\DeclareDocumentCommand\@mat{ g o d() d|| }
{
\IfNoValueTF{#1}
{
\IfNoValueTF{#2}
{
\IfNoValueTF{#3}
{
\IfNoValueTF{#4}
{()}
{\begin{vmatrix}#4\end{vmatrix}}
}
{
\begin{pmatrix}#3\end{pmatrix}
\IfNoValueTF{#4}{}{|#4|}
}
}
{
\begin{bmatrix}#2\end{bmatrix}
\IfNoValueTF{#3}{}{(#3)}
\IfNoValueTF{#4}{}{|#4|}
}
}
{
\begin{matrix}#1\end{matrix}
\IfNoValueTF{#2}{}{[#2]}
\IfNoValueTF{#3}{}{(#3)}
\IfNoValueTF{#4}{}{|#4|}
}
\ifnum\z@={\fi}
}


This macro works like an overloaded function with different argument delimiters {}, [], (), or ||. There are probably many reasons why this is a terrible idea --- so please, post your comments below. I would like to hear from those who know better than me.

• Is using {\bmat[1&0\\0&1]} an acceptable option? – Scott H. Nov 30 '12 at 21:14
• Welcome to TeX.SX! It's not at all clear why you should want \bmat to take its argument in brackets. – egreg Nov 30 '12 at 21:15
• the "inner" &s really have to be hidden, so putting them in braces is essential. – barbara beeton Nov 30 '12 at 21:25
• Thank you all for your helpful replies! Indeed, it is best to group everything within braces as Scott H. and barbara beeton have suggested. To that end, egreg provided a very sneaky way to insert the grouping braces I need to make it work the way I would like. – Sergio Dec 3 '12 at 15:01
• @Sergio I've added to my answer how I would manage the thing, rather than with that complex macro. – egreg Dec 3 '12 at 17:50

You can do it, but I strongly recommend you not doing it. You gain nothing in using [ and ] as delimiters for a mandatory argument.

\documentclass{article}
\usepackage{amsmath}

\makeatletter
\newcommand\bmat{{\ifnum\z@=}\fi\@bmat}
\def\@bmat[#1]{\begin{bmatrix}#1\end{bmatrix}\ifnum\z@={\fi}}
\makeatother

\begin{document}
$\bmat[1&0\\0&1]$
\begin{align}
M &= \bmat[1&0\\0&1]
\end{align}
\end{document}


(Note: I have reduced the text width to shorten the output.)

When you understand the trick, you are allowed to use it. :) Hint: look for \eegroup in Appendix D of the TeXbook.

Defining \mat to look for the next token in order to decide a delimiter is, in my opinion, wrong. I'd do like this:

\newcommand{\mat}[2][]{\begin{#1matrix}#2\end{#1matrix}}


so that

\mat{1&2\\3&4}    % no fences
\mat[b]{1&2\\3&4} % brackets
\mat[p]{1&2\\3&4} % parentheses
\mat[v]{1&2\\3&4} % vertical lines
\mat[V]{1&2\\3&4} % double vertical lines
\mat[B]{1&2\\3&4} % braces


would do even better, with minimum hassle and better readability.

Personally, I would stick with the long form, but it's a matter of taste.

• This is exactly the sort of trick I was looking for. Thank you for the TeXbook reference, it proved to be very enlightening. It also makes me feel better that this trick is in the TeXbook itself! I will append my initial entry with the final implementation the macro I was working on using your trick. I would be interested in your opinion of my macro, as it may be controversial in terms of proper LaTeX syntax. – Sergio Dec 3 '12 at 15:06
• @Sergio I've already stated my opinion: you gain nothing (and probably lose something) by using such a syntax. – egreg Dec 3 '12 at 15:18

Why this happens is rather interesting, since of course the raw code

\begin{align}
M = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}
\end{align}


does work even though the array separators are not between braces. The reason is that bmatrix itself inserts braces specifically to make this work, and by the time scanning has reached the separators, TeX has already read the opening brace and knows that it should protect them.

In your code, it would seem that \bmat is replaced with its expansion, which is the above code, before the & is read, but in fact, the & has already been read at that point: it needs to be picked up by the macro argument grabber. That is to say, even though the & wasn't "executed" it was seen, and apparently, TeX will detect the end of an array cell even just when going through tokenization.

Because of this, there is no way to use the normal \newcommand\bmat[1][]{} syntax to define a command that "just works" in arrays; egreg's answer shows how you can roll your own that accepts the same syntax but sneakily inserts braces. It's probably also possible to make a \catcode change prior to scanning for the argument that would make [] into functional brace characters, but for all that effort, I suggest you redesign your macro not to use an optional argument instead. As a perhaps compromise, you can place braces around it: {\bmat[1&0\\0&1]}, as Scott H. suggested.

• Thank you for the detailed and helpful analysis of how LaTeX parses this code! I considered \catcode changes but decided to steer clear of anything that might cause confusing [} mismatched delimiters and the like. – Sergio Dec 3 '12 at 15:04
• Doing with changes to catcodes is made very problematic because of what the amsmath environments like align do, they scan their entire contents, hence no catcode can be changed by a macro therein. So simple approaches like \def\@pmat#1{#1\end{pmatrix}} \def\pmat{\begin{pmatrix}\catcode[=1 \catcode\]=2 \@pmat} which do work in inline and display math fail in amsmath environments. (there is a backtick texsx formatting problem in my comment that I don't know how to fix) – user4686 Dec 3 '12 at 18:01
• @jfbu: I should have known that. Is there anything that \@ifmeasuring doesn't make difficult? – Ryan Reich Dec 3 '12 at 19:29
• @RyanReich alas! I got bitten hard by that one some time ago, while attempting to produce a very very lightweight and minimal 'poor man beamer' for my private use to do presentations... – user4686 Dec 3 '12 at 20:11

Scanning data inside \halign is very special in TeX. Note, that \halign is primitive implemetation of bmatrix or align LaTeX environments.

When \halign is inside \halign, then all works, but macro expansions must be done carefully. The example from @Ryan's answer

\begin{align}
M &= \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}
\end{align}


does (roughly speaking) this at TeX primitive level:

\halign\bgroup #&#\cr
M &= \vbox\bgroup\halign\bgroup &#\cr 1&0\cr 0&1\cr\egroup\egroup
\cr\egroup


The \begin{bmatrix} is implemented as a macro which expands to \vbox\bgroup\halign\bgroup &#\cr (not exactly, but more actions of this macro is not important for our case) and \end{bmatrix} expands to \cr\egroup\egroup. The significant is that the macro \begin{bmatrix} does not read its parameter separated by \end{matrix}, that both parts of this LaTeX environment work "independently". The data 1&0\cr 0&1 is read at main TeX processor level as \halign inside \halign data.

There is totally different situation when a macro with parameter is read inside data of the outer \halign. The main rule when scanning a macro parameter is:

The & (or \cr) is interpreted as & (or \cr) only if the processing is inside { ... } pair, i.e. inside the pair of characters of catcode 1 and 2. And it is irrelevant if this pair is out of the macro (and does \bgroup...\egroup in the typesetting output for example) or this pair is used around a macro parameter as syntax rule of reading macro parameters. It means that both of these examples work:

\def\hh[#1]{\vbox\bgroup\halign\bgroup &##\cr #1\cr\egroup\egroup}

\halign\bgroup #&#\cr
M & = {\hh[1&0\cr 0&1]}
\cr\egroup


or

\def\hh#1{\vbox\bgroup\halign\bgroup &##\cr #1\cr\egroup\egroup}

\halign\bgroup #&#\cr
M & = \hh{1&0\cr 0&1}
\cr\egroup


On the other hand, when the rule above is not satisfied, then the & or \cr is never scanned "as is" in the parameter: it is replaced immediatelly to the second part of cell declaration finalized by \endtemplate which is forbidden when scanning of parameters. It means that this example does not work:

\def\hh[#1]{\vbox\bgroup\halign\bgroup &##\cr #1\cr\egroup\egroup}

\halign\bgroup #&#\cr
M & = \hh[1&0\cr 0&1]
\cr\egroup


Moreover, this is not all. All macros are expanded at the beginning of each data cell until non-expandable and non-space token is found. When the macro is expanded at this time then & is scanned "as is". So, it works:

\def\h[#1]{\vbox\bgroup\halign\bgroup &##\cr #1\cr\egroup\egroup}

\halign\bgroup #&#\cr
M & \expandafter=\h[1&0\cr 0&1]
\cr\egroup