Generally speaking, there are a few cases.
This answer is mostly meant for people who want to learn more about LaTeX programming. Most of the fixes require some programming knowledge.
1 The inner macro parses the argument by changing catcode.
For example, in \textbf{\verb+123+}
, \verb
cannot be put inside the argument of most other commands because \verb
changes the catcode to parse the argument, unless special measures are applied e.g. \cprotect
.
For example, those questions have this issue: 1 2
1.1 How to detect this case
Generally speaking such "verbatim-like" commands are clearly denoted. Read the part below if that's not sufficient.
You can also test it by try putting it inside the argument of some harmless outer macro.
\newcommand\harmless[1]{#1}
\framebox{123} % typesets 123, okay
\harmless{\framebox{123}} % still works
\verb+123+ % works okay when alone
\harmless{\verb+123+} % error!
1.2 How to fix it
The cprotect package can be used in most cases, see the link above.
2 The inner macro is not expandable.
This case happens when you intend for some "function" to result in some "value" and pass that "result" to the "outer function"; but the inner is not expandable.
For example (adapted from \IfSubStr does not work when used as parameter of another macro)
\IfSubStr{ab}{a}{true}{false} % works, result in "true" because "ab" contains "a"
\StrLeft{true}{1} % works, result in "t"
\StrLeft{\IfSubStr{ab}{a}{true}{false}}{1} % mysterious error
2.1 How to detect this case
This requires some (La)TeX programming knowledge.
Determine whether the commands are expandable by using e.g. \tl_show:x
.
For the specific example above it would be done as the following
\ExplSyntaxOn
\tl_show:x{\IfSubStr{ab}{a}{true}{false}}
\ExplSyntaxOff
If it were expandable, you would see true
being logged. However, it gives an error, which means the command is not expandable i.e. this case is the error.
2.1.1 A special case
If your code consist of some function that you wrote yourself, that has two parts...
\newcommand \myfunction[1]{
% part 1: something to **set some variable**, which could be...
\pgfmathparse{1+1} % e.g. 1 2 3, or...
\DTLgetvalue{\thevalue}{database}{shortcut}{something} % e.g. 1, or...
\FPpow \myresult{2}{3} % e.g. 1, or...
\regex_replace_all:nnN {a} {b} \mytokenlist
% part 2: use that "variable"
\myresult % or...
\pgfmathresult % or...
\tl_use:N \mytokenlist
}
then it's definitely this case. Usually fixing it involves separating the first part to outside:
\outerfunction{\myfunction} % fail, because it's the same as...
\outerfunction{\pgfmathparse{1+1} \pgfmathresult}
% ^^^^^^^^^^^ part 1
% ^^^^^^ part 2
If you move the first part outside like this it might actually work:
\pgfmathparse{1+1} \outerfunction{\pgfmathresult}
% ^^^^^^^^^^ part 1
% ^^^^^^^ part 2
2.2 How to fix it
You have to look at the documentation to find other variants that allow you to pass the result to other commands.
In this particular example, it can be done by replacing the code in the "true" and "false" case with
\ExplSyntaxOn
\IfSubStr{ab}{a}{
\tl_set:Nn \myresult {true}
}{
\tl_set:Nn \myresult {false}
}
\ExplSyntaxOff
then \myresult
is expandable (it expands to true
in this case), and you can do \StrLeft{\myresult}{1}
safely.
Another example: If you want to pass the result of \StrLeft
to another macro, the documentation of \StrLeft
mentions that you can provide a trailing [⟨name⟩]
argument to store the result into that name. The rest is same as above.
Other examples of questions with this issue:
1
2
3
3 The outer macro does not expand its arguments.
In a "normal programming language", "functions" are "evaluated" from inside to outside, so if sqrt(4) = 2
then f(sqrt(4))
and f(2)
has the exact same behavior.
In TeX, however, the argument is passed verbatim to the outer command. As such, if the outer command decide to not expand the argument, it will make a difference.
Example question: 1 2 3 4
3.1 How to detect this case
Use the method mentioned above (e.g. \tl_show:x
) to check if the inner macro is really expandable.
If it is, try substituting the expansion result (with the correct token catcodes! Be careful) into the argument of the macro to see if it works.
3.2 How to fix it
Fixing this case also requires some LaTeX knowledge (although generally speaking simpler than the case 2. above), you can arrange for your custom command to expand to the whole code that is intended to be executed.
For example, in the example question 3 above, when the user write \tl_trim_spaces:n { \tl_tail:N \l_tmpa_tl }
, the content that is being trimmed is actually \tl_tail:N \l_tmpa_tl
, instead of the result of such.
In that case, you can explicitly arrange the expanded content (i.e. the tail) to be passed to the macro \tl_trim_spaces:n
, one method is to change the outer :n
to :e
. Other methods including \exp_args
or ExpandArgs
.
4 The outer macro "incorrectly expands" its arguments. (the inner macro is fragile.)
Similar to the case above. If the outer macro attempts to expand the inner one in an expansion-only context, and the inner macro is fragile, it will not behave correctly.
This case is already extensively covered in What is the difference between Fragile and Robust commands? When and why do we need \protect? .