There exists some consensus on the fact that one shouldn't use $...$, \(...\) or \ensuremath inside of newcommand (or NewDocumentCommand, ...) definitions to wrap passed macro arguments that are to typeset in math mode:
\documentclass{article}
\begin{document}
\newcommand*{\somecommand}[1]{
(#1)
}
\somecommand{Hello!}
\end{document}
Here, Hello! will be typeset as math, but the user/author of that macro might have never known what hit them. This leaves something to be desired and might be pretty surprising to the user. It should work, for example, like \somecommand{$Hello!$}, where the \newcommand definition omits entering math mode. The author should be in command and be aware of math and text modes.
Now, I have a command that is supposed to combine the macro argument input with more math, in this case a single equal sign:
\documentclass{article}
\begin{document}
\newcommand*{\somecommand}[1]{
#1(=)
}
\somecommand{(A)}
\end{document}
But this destroys math spacing:
We effectively have \(A\)\(=\). That makes little sense. We can't define \somecommad to have \(#1=\): this works when #1 is just text, but not if it's math mode already (LaTeX Error: Bad math environment delimiter). That case would expand to \(\(A\) = \).
The question is: why does that not work?
The advantage of \( over $ is the clear possibility to match left and right delimiters. From the \ensuremath example linked above, it's obvious why the equivalent with $ doesn't work: $ $A$ = $ is legal syntax ($$A$ = $ is not), but utterly useless (A is in text mode). Why does nesting the LaTeX inline math delimiters \( not work? It could just ignore any inner \(...\) pair. Technically, there's no reason it should/could get confused (as opposed to $) (?).
To solve the above problem (have a macro that appends an equal sign/some more math to its input), there seem to be only two solutions, both bad:
- Have
#1\(=\), destroying spacing and requiring manual intervention like\,. - Have
\(#1=\). Spacing works, but the input argument has to be in text mode. This is terrible for IDEs, linters etc. (will mark math mode characters like underscores as wrong since they'll be used outside of math mode etc.).
EDIT: Some more context to the question was requested. That context is pretty long; I tried to keep it to a usable minimum.
I am creating a template for an exam, based on the exam class. The formatting requirements are:
- Top of the page has the question for that page.
- Below is a box to pen in the answer (or part of it) to that question. The type of said answer can be flexible (usually a collection of required equations or a drawing).
- Below that large box, there might be an unknown number (0, 1, ...) of further boxes to partition students' answers further.
- All of this together should fill the current page vertically, automatically.
- Eventually, after the exam concluded, a solution sheet will be made available for future reference. The answers should, thanks to
exam's facilities, be toggleable and filled in "automatically". This is the core issue that lead to the original question above (caller should enter arguments in math mode, but that "destroys" spacing aka we need manual intervention to get spacing right, but I wondered because nesting\(...\)should technically be possible arbitrarily)
Thanks to tcolorbox, the above can be achieved pretty nicely:
\documentclass[
% answers,
]{exam}
\usepackage[
raster,
skins,
% theorems,% math key
]{tcolorbox}
\usepackage{amsmath}
\usepackage{etoolbox}
\NewDocumentCommand{\answerblock}{mmmm}{%
\tcbitem[
raster multicolumn=3,% Full width
title={Rearranged equation (symbolic)},
equal height group=A,% All boxes in this group are forced to the same height
% No matter the boxes natural height, conditionally leave some space for manual
% answer entries:
minimum for current equal height group=2cm,
]
% Problem: the caller will pass the argument already set in math mode (set
% inside \( ... \) -> DO NOT USE $...$). See also
% https://tex.stackexchange.com/q/34830/120853 .
% I asked about this here:
% https://tex.stackexchange.com/q/593218/120853
% We cannot put that argument inside another outer pair of \(...\), it
% will break.
% This forces use to chain multiple math environments. This breaks the natural
% spacing these have. Very sad and ugly, but we can hack a bit of space via
% \,.
% NOTE: DO NOT put text spaces here, they will show up in the result, since
% we are in text mode in between the multiple math environments.
#1({}={})\ifprintanswers#2\fi%
\tcbitem[
raster multicolumn=2,
title={Rearranged equation (numerical values)},
equal height group=A,% All boxes in this group are forced to the same height
% math,% from theorems; same issue with nested math ('Bad math environment delimiter')
]
#1({}={})\ifprintanswers#3\fi%
\hfill{}(=)%
\tcbitem[
raster multicolumn=1,% 'Right' width
title={Result},
equal height group=A,% All boxes in this group are forced to the same height
halign=center,
]
\ifprintanswers#4\fi%
}
\NewDocumentEnvironment{solutionoranswerarea}{%
d()% Force top box to this height, as a percentage of the remaining space
O{% Top box descriptive text
Collection of required equations%
}
+b% Environment body itself
}{%
\begin{tcbitemize}[
raster equal height=rows,% Core functionality to automatic sizing, see below
raster every box/.style={
valign=center,% Alignment for content inside of boxes
},
]
% The following is just an adaption from '15.6.2 Placing Spaces', see tcolorbox
% manual page 312, version 4.42 (2020-10-09).
% The idea is that we have two (the default) columns in an outer raster, each with
% only a single row: 2 columns, 1 row.
% However, the rows consist of tcbitemize environments, hence we can 'cheat' by
% having two such inner environments in which we can put however many columns and
% rows as we please.
% The raster equal height=rows makes it so both outer rows are forced to the
% same height through a couple iterations (=compilations); within the inner
% tcbitemize, we can use a dynamic space macro to adjust a height to match said
% outer height automatically (space to and add to natural height).
\tcbitem[% First column (of default 2)
blankest,% blankest style sets everything to empty/0pt
space to=\dynamicfillspace,% (Forced height - natural height) -> save to macro
]
\begin{tcbitemize}[% Nested itemization
raster width=2\linewidth,% 2 columns, so double back to original value
raster columns=3,% Full width
]
\tcbitem[
raster multicolumn=3,% Full width
add to natural height=\dynamicfillspace,
title={#2},
]
#3
\end{tcbitemize}
\tcbitem[blankest]% Second column
\begin{tcbitemize}[% Second nested itemization
raster width=0pt,% 'Hide' this entirely
]
\tcbitem[% Fixed height to which the first column will adapt (in height)
blankest,% blankest style sets everything to empty/0pt
height=\textheight-\pagetotal,% https://tex.stackexchange.com/a/207782/120853
]
\end{tcbitemize}
}{
\end{tcbitemize}
}
\begin{document}
\begin{questions}
\question Please solve for (d). Write your answer below. Note the required fields.
\begin{solutionoranswerarea}
\begin{solution}
\begin{align*}
a + b &= c \\
c &= 2d \\
\end{align*}
\end{solution}
\answerblock{\(d\)}{\(\frac{a + b}{2}\)}{\(\frac{20 + 10}{2}\)}{15}
\end{solutionoranswerarea}
\clearpage
\question Please solve for \(x\) \emph{and} \(y\).
\begin{solutionoranswerarea}
\begin{solution}
\begin{align*}
z &= x \\
z &= y^{2} \\
\end{align*}
\end{solution}
\answerblock{\(x\)}{z}{9}{9}
\answerblock{\(y\)}{\(\sqrt{z}\)}{\(\sqrt{9}\)}{3}
\end{solutionoranswerarea}
\clearpage
\question Please draw something.
\begin{solutionoranswerarea}[Drawing]
% Automatic solution requires extra work...
\end{solutionoranswerarea}
\end{questions}
\end{document}
Prints (lualatex on TeXLive 2020; requires a couple runs to get automatic spacing right):
With the answers class option on, it prints (last page is identical, solution to that is not part of this "M"WE):
Things that don't work to remedy this:
- Using the
tcolorboxtheorems library with itsmathkey. It leads to the same issue withBad math environment delimiterwhen nesting - We cannot do
\(\answerblock{}{}{}{}\)or even\(\tcbitem ...\), aka wrapping the entire macro in math mode.
Question as above still stands:
the caller aka author should be in control of when to enter math mode for various reasons. Hence, we get \(...\) arguments to answerblock. Text input like \answerblock{...}{...}{\sqrt{9}}{...} is not acceptable (surprising, opaque behaviour and confuses syntax highlighting/linting). \(\answerblock{}{}{}{}\) would be okay but does not work, so we also cannot use \TextOrMath, sadly.
So, manual intervention for spacing is required, since we apparently cannot nest \(...\). Why is that so and is there a way around this? \(...\) should be nestable arbitrarily.








\newcommand*{\somecommand}[1]{#1\({}=\)}but I'm not quite sure about the purpose... what about spacing after the equal sign? – campa Apr 16 '21 at 18:06\toks0={\(A\)\(=\)}and then postprocess the register with Lua to nuke the extra inline math tokens. – Henri Menke Apr 16 '21 at 18:09#2that gets appended to the equal sign. Space on that side should also work. This is for an exam template with empty to-be-filled fields, hence it's not a usual typographic use-case. @Henri Menke, this is already usinglualatexactually. I've done the Lua part of lualatex a couple times before, it works well. Thanks for the heads-up. – Alex Povel Apr 16 '21 at 18:22\mathrelspacing. Of course you must be careful about other spaces passed with the arguments. – campa Apr 16 '21 at 18:28\(\somecommand{A}\). If the command is clearly intended for math only then that can be expected from the user, similar to other, predefined math commands. – Marijn Apr 16 '21 at 20:47#1=? tex commands should almost always be math-only or text-only. Note if your command is for use in text you need to remove all the white space in the defintion. – David Carlisle Apr 16 '21 at 21:42#1\(=\)destroys spacing but\(#1=\)spacing works, but in both these cases you get a = with mathord class and no space, to get a relational = you would need\(#1={}\)or#1\({}={}\)(I would use$not\(in a definition) – David Carlisle Apr 16 '21 at 21:53