Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Is it okay for a control sequence \foo to include another control sequence \slurp which slurps more arguments than \foo actually passes to it?

For example, is it okay to do this:

\documentclass{article}

\newcommand\foo  [1]{#1 \slurp}    
\newcommand\slurp[3]{#1 #2 #3}

\begin{document}
\foo{a}{b}{c}{d}
\end{document}

Instead of this?

\documentclass{article}

\newcommand\foo  [4]{#1 \slurp{#2}{#3}{#4}}    
\newcommand\slurp[3]{#1 #2 #3}

\begin{document}
\foo{a}{b}{c}{d}
\end{document}
share|improve this question
    
It's a usual practice: take for example the definition of \captionof in the capt-of package: \newcommand\captionof[1]{\def\@captype{#1}\caption}. –  Gonzalo Medina Apr 3 '13 at 5:24
    
Every macro that has a starred version or an optional argument works this way. Even \newcommand itself is strictly speaking a macro without arguments. –  Qrrbrbirlbel Apr 3 '13 at 5:30
add comment

2 Answers

up vote 18 down vote accepted

Is this okay? Yes indeed! In fact, there is an abundance of usages for such macro definitions. Most notably the fundamental definitions for starred variants of commands. For example, article defines \section as

\newcommand\section{\@startsection {section}{1}{\z@}%
                                   {-3.5ex \@plus -1ex \@minus -.2ex}%
                                   {2.3ex \@plus.2ex}%
                                   {\normalfont\Large\bfseries}}

See how it takes zero arguments, even though we typically specify/use it as \section[<toc>]{<title>}?! That is because \@startsection takes 6 arguments, and then does a test to see whether the user added a star or not. From latex.ltx:

\def\@startsection#1#2#3#4#5#6{%
  \if@noskipsec \leavevmode \fi
  \par
  \@tempskipa #4\relax
  \@afterindenttrue
  \ifdim \@tempskipa <\z@
    \@tempskipa -\@tempskipa \@afterindentfalse
  \fi
  \if@nobreak
    \everypar{}%
  \else
    \addpenalty\@secpenalty\addvspace\@tempskipa
  \fi
  \@ifstar
    {\@ssect{#3}{#4}{#5}{#6}}%
    {\@dblarg{\@sect{#1}{#2}{#3}{#4}{#5}{#6}}}}

As such, the arguments we typically specify for \section is only gobbled by a macro two stages down the road.

Another good example of why this is good practice has to do with changes in category codes. Once arguments are consumed for use, their category codes are not changeable. So, a helper macro is usually used to modify the category codes before gobbling any arguments.

There are numerous other examples in the LaTeX kernel, from basic font macros to dealing with the ToC, even to defining a new command via \newcommand:

\def\newcommand{\@star@or@long\new@command}

Again, another macro that doesn't take any argument, but performs some operation prior to passing the torch to another macro. In general, this principle is well-used throughout the kernel and packages.

share|improve this answer
    
But what if the helper macro changes so that now it takes one more argument than before? Wouldn't that be a problem if the "helped" macro didn't reflect that change? –  nicolai.rostov Apr 3 '13 at 5:43
1  
@nicolai.rostov: Yes, if the helper macro now gobbles an additional argument while subsequent macros still require the same amount, you have to update the helper macro to pass the gobbled argument on. However, for the macro-writers, making a chance to a helper macro comes with the consequence of making updates throughout. That's why certain packages that have been around for very long isn't just updated in terms of its interface; things are kept as-is to avoid making things that formerly worked now crash... –  Werner Apr 3 '13 at 5:48
    
@Wernes: I would then have to update the helped, not the helper, macro. Right? –  nicolai.rostov Apr 3 '13 at 5:53
2  
@nicolai.rostov: You would have to update whatever is affected. I'm not entirely sure about the context though. If this is just your set of macros, then you can update things as you wish. If this deals with a package, then you have to put in some forethought as to the helper macros and how things fit together so that things could remain unchanged. –  Werner Apr 3 '13 at 5:56
    
Very constructive discussion –  karathan Apr 3 '13 at 5:59
show 2 more comments

As explained in Werner's answer, this is common practice. All macros having a *-variant are defined in this way:

\newcommand{\foo}{\@ifstar{\@sfoo}{\@foo}}
\newcommand{\@sfoo}[1]{Foo with * applied to #1}
\newcommand{\@sfoo}[1]{Foo without * applied to #1}

or variants thereof. Similarly, macros having more than one optional argument, such as \makebox must take a long route for deciding whether there are no, one or two optional arguments:

\newcommand{\bar}{\@ifnextchar[{\@bar@i}{\@bar}}
\def\@bar@i[#1]{\@ifnextchar[{\@bar@ii{#1}}{\@bar@iii{#1}}
\def\@bar@ii#1[#2]#3{Bar has two optional arguments, #1 and #2, and #3}
\def\@bar@iii#1#2{Bar has one optional argument, #1, and #2}
\def\@bar#1{Bar has no optional argument and #1}

With xparse the situation is quite different: since *-variants and optional arguments can be specified in a fairly general way, it's preferred to load all actual arguments:

\usepackage{xparse}
\NewDocumentCommand{\foo}{sm}{%
  \IfBooleanTF{#1}
    {Foo with * applied to #2}
    {Foo without * applied to #2}%
}

\NewDocumentCommand{\bar}{oom}{%
  \IfNoValueTF{#1}
    {Bar has no optional argument and #3}
    {\IfNoValueTF{#2}
       {Bar has one optional argument, #1, and #3}
       {Bar has two optional arguments, #1 and #2, and #3}%
    }%
  }%
}

This is "the future" with LaTeX3.

share|improve this answer
    
So, are you saying people will have to drop the common practice with LaTeX3? –  nicolai.rostov Apr 3 '13 at 8:36
1  
@nicolai.rostov At the 'document' level yes as it tends to make it hard to see what the 'true' nature of a command is. For code internals there can be advantages to it (it's faster and can read more cleanly in some cases). –  Joseph Wright Apr 3 '13 at 8:38
2  
Also, if you use \show to reveal what \NewDocumentCommand is doing under the hood, you'll discover that \foo and \bar are zero-argument wrapper macros that invoke various internal-to-xparse helpers to process the argument list, and then feed the processed args to \foo code / \bar code (with the space, yes) which always take the total possible number of arguments. So it's the same technique, just automated and systematized relative to what was done by hand in the 2e kernel. –  Zack Apr 3 '13 at 16:18
1  
@Zack Right. But the user doesn't need to know. ;-) –  egreg Apr 3 '13 at 16:23
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.