2

I had just a little question about LaTeX commands, and I haven't found anything about it despite searchs. We can easily pipe commands by simply writing one in the other. For example, if \a takes one argument, we can chain \a and \b simply by writing:

\a{\b{...}}

where the dots are the arguments passed to \b.

Now, let's suppose that \a has n arguments and that \b produces a result that could be divided into n outputs computed by the \b1, ..., \b commands. We would like to compose those two commands, just like we did in the simpler case where n = 1. I naively thought that defining \b according to the abstract scheme \newcommand{\b}[N]{{\b1{...}}...{\bn{...}}} would do the job as it does from a formal point of view.

However, this doesn't seem to work, as the following example with n = N = 2 does not provide the desired output:

\documentclass{article}

\newcommand{\tunnel}[2]{{#1}{#2}}
\newcommand{\name}[2]{\textbf{#1}~\textit{#2}}

\begin{document}

Desired output is \name{hello}{you}

Actual output is \name{\tunnel{hello}{you}}

\end{document}

When compiling with the xelatex test.tex command, I get the result

This is XeTeX, Version 3.14159265-2.6-0.999991 (TeX Live 2019/Arch Linux) (preloaded format=xelatex)
 restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2019-10-01> patch level 1
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2019/08/27 v1.4j Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo)) (./test.aux)
Runaway argument?
{
! Paragraph ended before \text@command was complete.
<to be read again> 
                   \par 
l.11 

? ! Too many }'s.
<recently read> \egroup 

l.11 

? [1] (./test.aux) )
Output written on test.pdf (1 page).
Transcript written on test.log.

The pdf produced has the following content:

Do you know how could one define \b (or \tunnel in the example) to make it work that way? I know this may not seem elegant, but I really need this kind of tweak for a more complex document. Thank you in advance!

4
  • 1
    TeX digests and expands code token by token. Therefore, if you want the arguments of a macro in a particular form, you may need to expand the argument before you execute the macro (see \expandafter primitive for example). Even in your "simple" \a{\b{...}} example, there is no guarantee that \a will be happy with \b{...} as an argument. In fact, it often will not be. Apr 24, 2020 at 14:44
  • The mechanics of expansion, while straightforward in theory can be tedious in practice. In your example, the proper invocation would be \expandafter\name\tunnel{hello}{you} Apr 24, 2020 at 14:53
  • Ok, I didn't knew that even \a{\b{...}} with n = 1 could fail! Thanks for your answer, really appreciate it. I'll take a look at the \exandafter command.
    – Wirius
    Apr 24, 2020 at 16:07
  • @Wirius in the n=1 case the argument of \a is literally \b{...} not the expansion of \b so it will fail or not depending if it is expecting commands or simply text. (actually I can think of no computer programming language or formal system that works the way you seem to expect where you can put a term as the first argument of a function and it supplies values for the first and second arguments.) Apr 24, 2020 at 16:15

3 Answers 3

3

As you know that \tunnel expands in a single step to two brace groups, you can expand it before calling \name

\documentclass{article}



\newcommand{\tunnel}[2]{{#1}{#2}}
\newcommand{\name}[2]{\textbf{#1}~\textit{#2}}

\begin{document}

Desired output is \name{hello}{you}

Actual output is \expandafter\name\tunnel{hello}{you}

\end{document}

But this depends heavily on the implementation details of \newcommand, if for example \tunnel had been defined by

\newcommand{\tunnel}[3][]{{#2}{#3}}

with an unused optional argument, or if it had been defined by xparse package \NewDocumentCommand then it would not complete in a single expansion step and so the \expandafter shown here would not produce the two brace groups needed by \name.

8
  • Ok, thank you very much! In the case of the document I'm writing, I would thus have to add a lot of \expandafter I guess... Do you think that in that case I could just use \newcommand{\ab}[N]{\expandafter\a\b{...}} to define some "rightly expanded composition of a and b"? Are you aware of any better alternative? For example, would it be possible to declare in the preamble that \b is always expanded before \a and use the code I provided in the question (just like one can write using namespace std in C++ and then don't have to specify the namespace anymore)?
    – Wirius
    Apr 24, 2020 at 16:14
  • 1
    @Wirius what you are asking is not like using using in C++ it is like calling a function with prototype requiring two arguments f(a,b) but calling it with one argument f(z()) were z returns a tuple and hoping that that tuple is taken as the argument list of f. Apr 24, 2020 at 16:18
  • Indeed, the situations are different. However, my message had (i) a solution to prevent the massive use of \expandafter using new definitions of commands which are "rightly composed" of the already existing ones and (ii) a question wether there was alternatives, and for example a way to set the fact that \a is always expanded after \b. I used the namespace example only to illustrate the idea that such an alternative would consist of one definitive configuration in the preamble. I therefore ask the question again: are there such alternatives, or other interesting ones?
    – Wirius
    Apr 24, 2020 at 16:32
  • @Wirius I didn't answer that comment as to be honest I couldn't understand your fragment of code. Also it depends on the actual use case, most commands do not work in a single expansion step and most do not work by expansion at all, anything that makes any assignments will not work by expansion, so whether it's possible to hide the expansions in a preceding macro depends on what the commands are and whether expandafter does anything useful at all. If you posted a real use case it would be easier to answer. Apr 24, 2020 at 18:54
  • 1
    @Wirius a possibly more useful technique is to pass in the command to process the arguments. instead of defining \b to return two brace groups give it an argument and define it to return #1{..}{...} then if \a is your command taking two brace groups \b\a expands to \a{...}{...} and does what you want. Then it doesn't matter how \b is defined or how many expansion steps it takes. Apr 24, 2020 at 19:45
3

Your comment

Thank you for your answer! That "lambda-caculusish" trick has been mentionned in the discussion below the accepted post, but thanks anyway for mentionning it. Furthermore, the idea of having optionnal arguments to have a two-sided funtion, 50% normal, 50% "functorized" is absolutely great. Thanks for the tip!

caused me to add a few remarks to my answer:

You program in LaTeX. LaTeX is based on TeX as described in the TeXbook of Donald Ervin Knuth. Basically LaTeX is just a set of macros written in TeX and packaged as a so-called format in order to have these macros loaded automatically when loading the TeX-program via an executable that is called latex/latex.exe/whatsoever.
Low-level-concepts introduced in the TeXbook also apply to LaTeX.
Thus in the following explanations things where I use the phrase "TeX" also apply when programming in LaTeX.

In my explanations below I nowhere use the word "function". I don't do so because—desite expl3's misleading (in my opinion ;-) ) terminology where the word "function" is used a lot—the programming-paradigma of TeX is not a procedural/functional one as is the case with high level programming languages like Pascal, C++ or Java, but is rather a macro-based declarative and symbolic one, where the symbols are formed by so-called tokens and where symbols/tokens get replaced by other symbols/tokens during the stage of expansion.

When I started with TeX and LaTeX, I tried to immediately transfer the things I knew about programming in higher languages ​​like Pascal, C ++ and Java to TeX/LaTeX.
I didn't do myself a favor with it. It made my TeX/LaTeX-learning-curve unnecessarily steep.
At that time it was not clear to me that the transfer should go hand in hand with transferring the concepts and terms of one programming paradigm to another completely different programming paradigm.
I think such transferring-attempts cannot work well when still in a stage of learning and getting familiar with the concepts and terms of one of the programming-paradigms involved.
I think such transferring-attempts only work well if you are very familiar with the concepts and terms of both programming paradigms.
That is why I believe that it is better, at least at the beginning of learning TeX/LaTeX, to stick strictly to the terms that belong to the programming paradigm underlying TeX/LaTeX and to avoid "borrowing" terms that belong to other programming paradigms.

In Knuth's analogy to a digestive process TeX has

  • eyes
  • a digestive tract
  • the ability to produce tokens and put them into its mouth where the digestion process begins.

TeX's eyes read the .tex-input-file. Hereby TeX takes the input for a set of instructions for producing tokens and putting these tokens into its mouth one by one. So the tokens form a "token-stream" whose elements one by one are lead through TeX's digestive tract. Tokens can be control-sequence-tokens in different flavors or character-tokens in different flavors.

Producing tokens from the instructions formed by .tex-input-files takes place according to rules provided by characters' category-codes, by values of parameters like \endlinechar and by things that are "hardcoded" into the TeX-program (or LaTeX-program, if you use the variant where the set of macros that forms the LaTeX-format is loaded automatically).

Expansion of a(n expandable) token—i.e., replacement of that token (and probably those tokens that form its arguments) by other tokens—takes place while tokens are transported through TeX's gullet.

Assignments (defining macros, assigning values to \count-registers and the like) in that analogy take place in TeX's stomach.

The final result of TeX's digestion-process will be the output-files (.pdf-file/.dvi-file, .log-file, auxiliary text files like the .aux-files and .toc-file and .lot/.lof-file etc) and things that are written to the console.

After this very rough overview of things let's go back to the stage of expansion of expandable tokens which takes place in TeX's gullet:

Think of TeX-macros as tokens that during expansion get removed from the token-stream and which additionally trigger the removal of more tokens from the token-stream followed by insertion of tokens into the token-stream. "Removal of more tokens from the token-stream" is done according to the rules provided by the ⟨parameter text⟩ which belongs to the macro's ⟨definition⟩. "Insertion of tokens into the token-stream" is done according to the rules provided by the ⟨balanced text⟩ which also belongs to the macro's ⟨definition⟩. The tokens inserted into the token-stream form the "replacement text".

With \newcommand{\b}[N]{{\b1{...}}...{\bn{...}}} and with ... being the arguments of \b which are to be passed to macros \b1, .... , \bn you ask for \b1, .... , \bn to be changeable without further tricks although being tokens that are part of the ⟨balanced text⟩ of the definition of macro \b.

But this is not possible without further expansion-tricks:

Tokens [—other than the sequences #1, #2, ..., #9 that denote arguments that are to be gathered from the token-stream's subsequent tokens according to the ⟨parameter text⟩ and other than sequences ## that will be reduced to #, which is useful for nesting ⟨definition⟩s inside the ⟨balanced text⟩ of other ⟨definition⟩s—] that come from the ⟨balanced text⟩ of a macro's ⟨definition⟩ (as would be the case with \b1,...,\bn) do not get changed/replaced at the time when expanding that macro (in this case the macro \b) leads to inserting the replacement text into the token-stream.


What can be done by means of macros and macro-expansion?

You wish to apply \name to the replacement text of \tunnel?

Rephrased: Expanding \tunnel shall lead to the insertion of tokens into the token-stream that are to be used as arguments of \name ?

As already mentioned in a comment by David Carlisle to the accepted post and as in your reply to that comment called a "lambda-calculus-like method", you can define \tunnel with an argument where you can pass a macro-token which shall process the arguments delivered by \tunnel.

I now add that that argument can be an optional one which by default is empty:

\documentclass{article}

\newcommand{\tunnel}[3][]{#1{#2}{#3}}
\newcommand{\name}[2]{\textbf{#1}~\textit{#2}}

\begin{document}

Desired output is \name{hello}{you}

Actual output is \tunnel[\name]{hello}{you}

\end{document}

By means of a little helper-macro, called \PassArgumentToMacroAndThenDo, with \tunnel you can also pass things to more than one macro:

\documentclass{article}

\newcommand\PassArgumentToMacroAndThenDo[3]{%
  %#1 <Macro> which shall process argument #3 as its first/only argument.
  %#2 Tokens to insert behind the sequence <Macro>{Argument3} .
  %#3 Element of a list of Arguments that are to be processed by <Macro>s.
  #1{#3}#2%
}%

\newcommand\ProcessFirstArg[1]{\par\noindent First Arg processed by \texttt{\string\ProcessFirstArg}: #1.}
\newcommand\ProcessSecondArg[1]{\par\noindent Second Arg processed by \texttt{\string\ProcessSecondArg}: #1.}
\newcommand\ProcessThirdArg[1]{\par\noindent Third Arg processed by \texttt{\string\ProcessThirdArg}: #1.}
\newcommand\ProcessFourthArg[1]{\par\noindent Fourth Arg processed by \texttt{\string\ProcessFourthArg}: #1.}
\newcommand\ProcessFifthArg[1]{\par\noindent Fifth Arg processed by \texttt{\string\ProcessFifthArg}: #1.}
\newcommand\ProcessSixthArg[1]{\par\noindent Sixth Arg processed by \texttt{\string\ProcessSixthArg}: #1.}
\newcommand\ProcessSeventhArg[1]{\par\noindent Seventh Arg processed by \texttt{\string\ProcessSeventhArg}: #1.}
\newcommand\ProcessEighthArg[1]{\par\noindent Eighth Arg processed by \texttt{\string\ProcessEighthArg}: #1.}

\newcommand{\tunnel}[9][]{#1{\texttt{\string\tunnel}-processed-#2}%
                            {\texttt{\string\tunnel}-processed-#3}%
                            {\texttt{\string\tunnel}-processed-#4}%
                            {\texttt{\string\tunnel}-processed-#5}%
                            {\texttt{\string\tunnel}-processed-#6}%
                            {\texttt{\string\tunnel}-processed-#7}%
                            {\texttt{\string\tunnel}-processed-#8}%
                            {\texttt{\string\tunnel}-processed-#9}}

\begin{document}

\PassArgumentToMacroAndThenDo\ProcessFirstArg{%
  \PassArgumentToMacroAndThenDo\ProcessSecondArg{%
    \PassArgumentToMacroAndThenDo\ProcessThirdArg{%
      \PassArgumentToMacroAndThenDo\ProcessFourthArg{%
        \PassArgumentToMacroAndThenDo\ProcessFifthArg{%
          \PassArgumentToMacroAndThenDo\ProcessSixthArg{%
            \PassArgumentToMacroAndThenDo\ProcessSeventhArg{%
              \PassArgumentToMacroAndThenDo\ProcessEighthArg{%
              }%
            }%
          }%
        }%
      }%
    }%
  }%
}%
{A}{B}{C}{D}{E}{F}{G}{H}

\bigskip

\tunnel{A}{B}{C}{D}{E}{F}{G}{H}

\bigskip

\tunnel[%
  \PassArgumentToMacroAndThenDo\ProcessFirstArg{%
    \PassArgumentToMacroAndThenDo\ProcessSecondArg{%
      \PassArgumentToMacroAndThenDo\ProcessThirdArg{%
        \PassArgumentToMacroAndThenDo\ProcessFourthArg{%
          \PassArgumentToMacroAndThenDo\ProcessFifthArg{%
            \PassArgumentToMacroAndThenDo\ProcessSixthArg{%
              \PassArgumentToMacroAndThenDo\ProcessSeventhArg{%
                \PassArgumentToMacroAndThenDo\ProcessEighthArg{%
                }%
              }%
            }%
          }%
        }%
      }%
    }%
  }%
]{A}{B}{C}{D}{E}{F}{G}{H}

\end{document}

By means of yet another helper-macro-mechanism which is based on \ronannumeral0-expansion and which does recursively collect an arbitrary amount of arguments which can be specified by the user, you can arrange things in ways where each of the things that can be passed to macros by \tunnel do not necessarily need to consist of a single argument only:

\documentclass{article}

\makeatletter
\newcommand\PassKArgumentsToMacroAndThenDo[3]{%
  %#1 TeX <number>-quantity denoting the non-negative integer-number <K>.
  %#2 <Macro> which shall process next <K> Arguments.
  %#3 Tokens to insert behind the sequence <Macro>{Argument1}..{ArgumentK} .
  \romannumeral0\expandafter\PassKArgumentsToMacroAndThenDoLoop\expandafter{\romannumeral0\number\number#1 000}{#2}{#3}{}%
}%
\newcommand\PassKArgumentsToMacroAndThenDoLoop[4]{%
  %#1 Sequence of letters m in the amount of arguments to collect
  %#2 <Macro> which shall process next <K> Arguments
  %#3 Tokens to insert behind the sequence <Macro>{Argument1}..{ArgumentK}
  %#4 Arguments collected so far
  \ifx D#1D\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  { #2#4#3}%
  {\PassKArgumentsToMacroAndThenDoLoopFetchNextArg{#1}{#2}{#3}{#4}}%
}%
\newcommand\PassKArgumentsToMacroAndThenDoLoopFetchNextArg[5]{%
  %#1 Sequence of letters m in the amount of arguments to collect
  %#2 <Macro> which shall process next <K> Arguments
  %#3 Tokens to insert behind the sequence <Macro>{Argument1}..{ArgumentK}
  %#4 Arguments collected so far
  %#5 Next argument
  \expandafter\PassKArgumentsToMacroAndThenDoLoop\expandafter{\@firstoftwo{}#1}{#2}{#3}{#4{#5}}%
}%
\makeatother

\newcommand\ProcessFirstAndSecondArg[2]{%
  \par\noindent First Arg processed by \texttt{\string\ProcessFirstAndSecondArg}: #1.%
  \par\noindent Second Arg processed by \texttt{\string\ProcessFirstAndSecondArg}: #2.%
}%
\newcommand\ProcessThirdAndFourthAndFifthArg[3]{%
  \par\noindent Third Arg processed by \texttt{\string\ProcessThirdAndFourthAndFifthArg}: #1.%
  \par\noindent Fourth Arg processed by \texttt{\string\ProcessThirdAndFourthAndFifthArg}: #2.%
  \par\noindent Fifth Arg processed by \texttt{\string\ProcessThirdAndFourthAndFifthArg}: #3.%
}%
\newcommand\ProcessSixthSeventhAndEighthArg[3]{%
  \par\noindent Sixth Arg processed by \texttt{\string\ProcessSixthSeventhAndEighthArg}: #1.%
  \par\noindent Seventh Arg processed by \texttt{\string\ProcessSixthSeventhAndEighthArg}: #2.%
  \par\noindent Eighth Arg processed by \texttt{\string\ProcessSixthSeventhAndEighthArg}: #3.%
}

\newcommand{\tunnel}[9][]{#1{\texttt{\string\tunnel}-processed-#2}%
                            {\texttt{\string\tunnel}-processed-#3}%
                            {\texttt{\string\tunnel}-processed-#4}%
                            {\texttt{\string\tunnel}-processed-#5}%
                            {\texttt{\string\tunnel}-processed-#6}%
                            {\texttt{\string\tunnel}-processed-#7}%
                            {\texttt{\string\tunnel}-processed-#8}%
                            {\texttt{\string\tunnel}-processed-#9}}

\begin{document}

\PassKArgumentsToMacroAndThenDo{2}\ProcessFirstAndSecondArg{%
  \PassKArgumentsToMacroAndThenDo{3}\ProcessThirdAndFourthAndFifthArg{%
    \PassKArgumentsToMacroAndThenDo{3}\ProcessSixthSeventhAndEighthArg{%
    }%
  }%
}%
{A}{B}{C}{D}{E}{F}{G}{H}

\bigskip

\tunnel{A}{B}{C}{D}{E}{F}{G}{H}

\bigskip

\tunnel[%
  \PassKArgumentsToMacroAndThenDo{2}\ProcessFirstAndSecondArg{%
    \PassKArgumentsToMacroAndThenDo{3}\ProcessThirdAndFourthAndFifthArg{%
      \PassKArgumentsToMacroAndThenDo{3}\ProcessSixthSeventhAndEighthArg{%
      }%
    }%
  }%
]{A}{B}{C}{D}{E}{F}{G}{H}

\end{document}
5
  • Thank you for your answer! That "lambda-caculusish" trick has been mentionned in the discussion below the accepted post, but thanks anyway for mentionning it. Furthermore, the idea of having optionnal arguments to have a two-sided funtion, 50% normal, 50% "functorized" is absolutely great. Thanks for the tip!
    – Wirius
    Apr 24, 2020 at 23:25
  • Could you, please, distinguish between TeX and LaTeX? The whole text you wrote here is about TeX, no LaTeX. I don't understand why there is the preposition (La) in brackets.
    – wipet
    Apr 26, 2020 at 9:48
  • The preposition (La) seems ambiguous. Why do you not use preposition (Op), i.e. (Op)TeX in your text? OpTeX is just a set of macros programmed in TeX and packaged as so-called "format". All the text you wrote is true for OpTeX too, so (Op)TeX is good preposition for your text too. But it is ambiguous. Why do you prefer the preposition (La)?
    – wipet
    Apr 26, 2020 at 10:10
  • @wipet Why does it seem ambiguous? Knuth's TeXbook, where things like <definition> and <balanced text> are found, is about TeX. The questioner programs in LaTeX. So I think it is a good thing to indicate that things apply to LaTeX (which is based on TeX) too. And the other way round it is a good thing to indicate that things do not apply to LaTeX only, but to other flavors of TeX also. Apr 26, 2020 at 10:29
  • The (La) preposition is ambiguous because many people does not read it in such precise way as you explained in the comments and they can do that you describe the LaTeX features. But you describe the OpTeX features here too. I recommend to not to use the (La) preposition here.
    – wipet
    Apr 26, 2020 at 10:53
1

The \tunnel macro has no sense in your exmaple, because \tunnel{xx}{yy} is the same as {xx}{yy}. I modified your \tunnel in order it has a sense:

\def\tunnel#1#2{{A:#1}{B:#2}}
\def\name#1#2{{\bf#1}~{\it#2}}

\expandafter\name\tunnel{hello}{you}

\bye

The \expandafter used in this example does first level of expansion of the \tunnel, so we have \name{A:hello}{B:you}. Now, the \name macro does its work.

3
  • As David Carlisle already pointed out regarding \expandafter - quote from his comment: "[...] Also it depends on the actual use case, most commands do not work in a single expansion step and most do not work by expansion at all, anything that makes any assignments will not work by expansion, so whether it's possible to hide the expansions in a preceding macro depends on what the commands are and whether expandafter does anything useful at all. [...]" Apr 26, 2020 at 10:27
  • @UlrichDiez If I do \def\macro...{{...}{...}} as in my example, then the braces in the replacement text of the macro is accessible by single expansion step. There is no more dependencies.
    – wipet
    Apr 26, 2020 at 10:30
  • Your phrase "If I do" sort of indicates the dependency which David Carlisle's comment is about. Apr 26, 2020 at 10:40

You must log in to answer this question.

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