1

My question is about the design of commands that redefine other helper commands. In order to ask my question, I need to give a small set-up in order to describe what I mean. Let's take some helper commands from my personal preamble, with some minor changes for this question:

\DeclarePairedDelimiter{\parenth}{(}{)}

\providecommand{\intvar}{}
\ProvideDocumentCommand{\integral}{s m m e{_^}}{%
    \renewcommand{\intvar}{#2}
    \int%
    \IfValueT{#4}{_{#4}}%
    \IfValueT{#5}{^{#5}}%
    \IfBooleanTF{#1}{\parenth*{#3}}{#3}%
    \odif{#2}%
}

\ProvideDocumentCommand{\fourier}{s m m m E{_^}{{-\infty}{\infty}}}{%
    \IfBooleanTF{#1}{%
        \integral*{#2}{{#4}e^{-2\pi i{#2}{#3}}}_{#5}^{#6}%
    }{%
        \integral{#2}{{#4}e^{-2\pi i{#2}{#3}}}_{#5}^{#6}%
    }%
}

To save you the trouble of deciphering the commands, they take on the form \integral{variable}{body}_{from}^{to} and \fourier{var from}{var to}{body}_{from}^{to}, where the sub and super-scripts are optional. Now, notice my use of the command \intvar. I have added it to the \integral command definition for the sake of this question. Its purpose is to allow the user to get the variable of integration for an integral. As an example:

\begin{gather*}
    \integral{x}{f(\intvar) g(\intvar)}_{0}^{1}\\
    \fourier{t}{\xi}{f(\intvar)}
\end{gather*}

Gives us:

And if we change the integration variable, but not the body:

\begin{gather*}
    \integral{z}{f(\intvar) g(\intvar)}_{0}^{1}\\
    \fourier{\theta}{\xi}{f(\intvar)}
\end{gather*}

We get as we expect:

So, my question involves cases like this, where you can use, say \integral, within another \integral command (or derived command, as is the case with \fourier). Doing so will obviously mean that \intvar takes on the variable of that "level", as expected. As an example:

\begin{gather*}
    \integral{x}{f(\intvar)\integral{y}{g(\intvar)}}
\end{gather*}

Giving:

This code, however, is still rather dangerous and can produce cases where the document will never compile. Eg, putting \intvar here never compiles: \integral{x}{\fourier{\intvar}{...}{...}} since it attempts to set the value of \intval to \intval, which is then recursive. (How would this be handled so that this situation could never occur?)

My question: How do you correctly write macros with these kinds of inner-defined commands? In honesty, I have never thought of such a case of needing this. But this example is a fun and interesting one I thought of that I would like to come to understand more of.

Would this be done through more careful design considerations and testing, or through more advanced expl3? Are there packages that help to do these kinds of tasks?

Additionally, could you design a case where you can reference an \intvar from a higher level? For example, if I wanted to reference the variable of integration for the integral surrounding the current integral scope. In this case, \intvar would only be for that scope's integral variable. Is this something that can be done without extreme difficulty? And if so, how would someone even go about defining such a command (as in, what tools do they use: a package, lower-level macros, etc)? Are there other considerations that would have to be made?

8
  • 1
    You may be interested in TeX's grouping mechanism which is described in... maybe TeXbook chapter 5. That having said programming in TeX is usually complicated anyway
    – user202729
    Commented May 14, 2023 at 11:46
  • @user202729 Thanks! I haven't seen that term before, so it will likely be a new read. Thanks! Edit: Having looked at it, I have and just forgot the terminology lol Commented May 14, 2023 at 11:47
  • 1
    I would add a \begingroup...\endgroup scope to your \integral command so that \intvar is only in scope between \int and d... currently it is defined for all following code (so nested instances will only accidentally work) Commented May 14, 2023 at 11:48
  • so it may be a better idea to just explicitly write out what you want (x instead of \intvar) anyway. That having said what does the other example even mean (you can't integrate over the same variable twice right?)
    – user202729
    Commented May 14, 2023 at 11:48
  • 1
    you need it otherwise this is simply not true "Doing so will obviously mean that \intvar takes on the variable of that "level", as expected." there is no level or scope implied by macro definition Commented May 14, 2023 at 12:19

1 Answer 1

1

Use grouping.

Also code duplication can be reduced.

\documentclass{article}
\usepackage{mathtools}

\newcommand{\odif}{\mathop{}\!d}
\DeclarePairedDelimiter{\parenth}{(}{)}

\providecommand{\intvar}{}
\NewDocumentCommand{\integral}{s m m e{_^}}{%
    \begingroup
    \renewcommand{\intvar}{#2}%
    \int
    \IfValueT{#4}{_{#4}}%
    \IfValueT{#5}{^{#5}}%
    \IfBooleanTF{#1}{\parenth*{#3}}{#3}%
    \odif#2%
    \endgroup
}

\NewDocumentCommand{\fourier}{s m m m E{_^}{{-\infty}{\infty}}}{%
    \expanded{\integral\IfBooleanT{#1}{*}}%
    {#2}{{#4}e^{-2\pi i{#2}{#3}}}_{#5}^{#6}%
}

\begin{document}

\begin{gather*}
    \integral{x}{f(\intvar) g(\intvar)}_{0}^{1} \\
    \fourier{t}{\xi}{f(\intvar)} \\
    \integral{x}{f(\intvar)\integral{y}{g(\intvar)}} \\
    \integral*{x}{f(\intvar) + g(\intvar)}_{0}^{1} \\
    \fourier*{t}{\xi}{f(\intvar)} \\
    \integral{x}{f(\intvar)\integral*{y}{g(\intvar)+2}}
\end{gather*}

\end{document}

enter image description here

I cannot recommend using \parenth* indiscriminately, though.

1
  • Thanks for your input! I haven't seen that kind of refactoring. That is very interesting. I will admit that the \fourier command is something I only cooked up in the past day or two in order to reduce my own typing, so I haven't given any time to think about it's implementation. As for your final comment, can you elaborate? If you mean to say using \parenth* in the integral should be used only when needed, then that is exactly how I use it. I only do it when I have many large integral/summation combinations, so it is simply a helpful boilerplate remover, if necessary. Commented May 15, 2023 at 3:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .