15

For writing my own TeX code, grouping (\begingroup, \endgroup) helps on automatically saving and restoring TeX "variables".

(La)TeX "variables" are either macros or registers. The registers can be counters (integers), dimensions, skips (both fractional numbers with units) or read/write handlers etc. Note that you can change them inside a group, i.e. either { .. } or \begingroup ... \endgroup or a LaTeX environment, then their old value is restored at the end of the group automatically. (Martin Scharrer, How to save variables)

On the other hand, grouping is not expandable (since the primitives \begingroup and \endgroup are not expandable).

  1. How is TeX treating grouping? Does TeX only save and restore a specific variable when an assignment to it occurs? It seems when a global assignment (e.g. font assignment: \fontdimen, \hyphenchar, \skewchar; hyphenation assignment: \hyphenation, \patterns; box size assignment: \ht, \dp, \wd; ...) occurs that TeX omits saving and restoring.

  2. (EDIT) Considering a TeX command using 5 to 10 "variables". In other words, it is temporarily assigning different values to them, for example \baselineskip=30pt. Is there any good reason to avoid grouping in this situation? Or should grouping be used here because of above advantage?

1
  • Assignments are already non expandable, so it's unclear why "blocking expansion" should be a factor.
    – egreg
    Jan 24, 2013 at 17:48

4 Answers 4

7

Just to add a bit on the other answers and to try and answer the title of the question:

When to use or avoid grouping

As David mentioned it mostly depends on your use case but here are some typical areas where using grouping is advisable:

  1. When setting the \@elt in lists.

    \def\alist{\@elt a\@elt b\@elt c}
    \begingroup
      \let \@elt\@gobble  \alist
    \endgroup
    \begingroup
      \def\@elt#1{-#1- }  \alist
    \endgroup
    
  2. When defining macros that affect paragraph(s) of text.

    \def\startlines{%
    \begingroup
      \obeylines\obeyspaces
      \leavevmode
    }
    \def\endlines{\endgroup}
    
    \startlines
    
    This is a test 
    ...   ......         ....... test and another test
    
    This is a test 
    ...   ......         ....... test and another test
    
    \endlines
    
  3. When you want to split a command into two parts, for example:

     \def\index{\@bsphack\begingroup \@sanitize\@index}
     \def\@index#1{\endgroup\@esphack}
    
  4. When changing catcodes:

      \def\MyCatcodeMagic#1{%
        \begingroup
        \catcode`\ 10 %
        \ifnum \endlinechar<256 %
           ...
        \endgroup
       }
    
  5. In math commands to avoid `leaking out' of style changes

When to avoid grouping is mostly a matter of programming style. Good programming practice dictates that globals are an evil, however, is very hard to achieve this with TeX/LaTeX. The LaTeX source2e tends to use grouping for most of the important macros, even in cases such as:

       \def\frac#1#2{{\begingroup#1\endgroup\over#2}}

Can you guess why?

2
  • I think the grouping at \frac prevents usage mistakes and, hence, unexpected output. If the second { } group or the \begingroup - \endgroup group is missing, one can use $\frac{\mathtt}{2}$ or $\frac{1}{1\mathtt}=1$ without getting an error message about the missing argument to \mathtt.
    – e-birk
    Jan 25, 2013 at 23:01
  • Re: "Can you guess why?", see this answer
    – Jonathan H
    Jul 19, 2017 at 15:50
14

Some assignments do not respect grouping:

\hyphenation{<words>}
\patterns{<balanced tokens>}
\batchmode|\nonstopmode|\scrollmode|\errorstopmode|\interactionmode=<2 bit number

\hyphenchar<font>=<number>
\skewchar<font>=<number>
\spacefactor|\prevgraf|\deadcycles|\insertpenalties=<number>

Box dimension setting is special. When TeX acts on a box register it does respect grouping, but with respect to the last incarnation of the box. Example:

\setbox0=\hbox{\hskip 10pt}{\wd0=0pt}

will result in box 0 having width 0pt even if the assignment has been performed on a group. To the contrary,

\setbox0=\hbox{\hskip 10pt}{\setbox0=\hbox{def}\wd0=0pt}

will result in box register 0 having width 10pt, as it's readily verifiable.

This is similar to what happens when a box register is used:

\setbox0=\hbox{abc}{\box0}

will result in printing abc and the box register 0 to be empty also after closing the group. With

\setbox0=\hbox{abc}{\setbox0=\hbox{def}\box0}

"def" will be printed and the box register 0 will contain \hbox{abc} after closing the group.

Another special case is that of font loading: if one says

{\font\foo=somefont}

then \foo won't be accessible outside the group, but the font information will already have been stored in the font memory and not released.


What values does TeX store in the stack? Only those that are changed inside a group. There is the risk that the stack is exhausted when doing local assignments, but modern TeX implementations reserve at least 50000 words of stack memory, which is sufficient for most applications, provided programming is good, in the sense that the same "variable" is always acted upon locally or globally (so the save stack doesn't retain values it cannot release back).

Every environment in LaTeX forms a group, with the exception of the document environment: it would be absurd to do every assignment in the document proper inside a group, with nefarious effects on the save stack. But, apart from very special applications, it's quite difficult to imagine exhausting the save size.

Working with groups is in many cases easier, because it doesn't require storing the values to put back at the end. As to expandability, no assignment is expandable, so the fact that { and } or \begingroup and \endgroup are not expandable is completely irrelevant for the discussion.

So, if you have to completely change the \lccode vector for the whole Unicode range (in XeTeX or LuaTeX), then don't do it in a group, since you'd be doing 2^21 assignments. If you have to keep something into memory till the end of processing, then use global assignments that will survive the group structure and not pollute the save stack. If you want to change the margins for a list environment, use the already provided group structure.

10

For any non-global assignment TeX saves the old value on the "save stack" and restores it at the end of the current group. You can see how much save stack you have used in the log file

23i,4n,21p,148b,149s stack positions out of 5000i,500n,10000p,200000b,50000s
                 ^^^                                                  ^^^^^^

Your second question isn't clear, no assignment is expandable \let \def and register assignments are all non-expandable operations


The question about whether to use grouping or not isn't really answerable in that generality, whether or not you use a group depends mostly on whether you want to use a group or not, not on the details of which assignments are made.

In

\begin{empty}
....
\end{empty}

For environment is implemented by \empty which expands to nothing so doesn't do any assignments but the fact that the environment is a group means that any user code that goes in the ... is scoped by the group.

One time that I have needed to use global assignments and explicitly save and restore values in a privately implemented stack at the macro layer rather than use TeX grouping is in blockarray where the syntax

\begin{block}
....

\end{block}

is used but in reality the begin and end code are in different table cells so in different groups, unlike a real environment there is no group that starts at the beginning of the block and ends at the end. The comments in the code say

 \subsection{`Local' Variables}

 Most of {\tt blockarray} happens inside a "\halign" which means that
 the different parts have to make global assignments if they need to
 communicate. However many of these assignments are logically local to
 {\tt blockarray}, or a sub-environment like {\tt block}. This means
 that I have to manage the saving and restoring of local values `by
 hand'.

 Three different mechanisms occurred to me, I have used all of them in
 this style, mainly just to try them out!

You can look at the blockarray package document ion for the gory details.

1
  • Please see EDIT for my second question. I see now, expansion cannot be a reason. Can there be any different reason to avoid grouping except using too much stack?
    – e-birk
    Jan 24, 2013 at 18:41
4

Unlike in Java, Python, and other programming languages, in TeX the fundamental programming construct that is affected by grouping is not variable, but assignment, of which there are many kinds. Some assignments assign a value to a variable, i.e. to a control sequence, but others assign a value to an internal quantity. Not all assignments respect TeX's grouping. I will dedicate this answer to a complete description of TeX assignments. The descriptions have been compiled from The TeXbook (20th printing, Addison-Wesley 1991), to which the page numbers refer.

All TeX assignments have the same effect in all modes. Assignment commands often include an = sign, but in all such cases this sign is optional. (p. 275)

A given assignment is either global or local. An assignment is global if it is prefixed by \global or if it is one of the assignments on the list of global assignments below. Otherwise, the assignment is local.

Local assignments obey TeX’s grouping structure; i.e., the changed quantities will be restored to their former values when the current group ends. On the other hand, the effects of global assignments persist across grouping boundaries. (p. 277)

Every assignment can be prefixed by \global (p. 275), but the presence or absence of \global as a prefix has no effect on the assignments listed in the Global Assignments list below. (p. 277) A \gdef command is equivalent to \global\def, and \xdef is equivalent to \global\edef. (p. 276)

If the \globaldefs parameter is positive at the time of an assignment, a prefix of \global is automatically implied; but if \globaldefs is negative at the time of the assignment, a prefix of \global is ignored. If \globaldefs is zero (which it usually is), the appearance or nonappearance of \global determines whether or not a global assignment is made. (p. 275)

A noteworthy quirk of assignments that is unrelated to grouping, is that TeX takes precautions to ensure that constructions like ‘\chardef\cs=10\cs’ and ‘\font\cs=name\cs’, i.e. assignments in which the control sequence is used immediately after it is defined, won’t expand the second \cs until the assignments are done. (p. 278) This is contrary to TeX's normal rules of expansion, which guarantee, for instance, that the control sequence \after in

\def\after{0}%
\chardef\cs=1\after

will expand during \cs's definition, so that the ultimate effect will be the same as

\def\after{0}%
\chardef\cs=10

Global Assignments

The following is an exhaustive list of all of TeX's inherently global assignments. (pp. 277-278 & 271) The effects of these assignments transcend groupings.

  • font assignment
    • \fontdimen ... = ...
    • \hyphenchar ... = ...
    • \skewchar ... = ...
  • hyphenation assignment
    • \hyphenation ...
    • \patterns ...
  • box size assignment
    • \ht<8-bit number> = ...
    • \wd<8-bit number> = ...
    • \dp<8-bit number> = ...
  • interaction mode assignment
    • \errorstopmode
    • \scrollmode
    • \nonstopmode
    • \batchmode
  • intimate assignment
    • special integer assignment
      * \spacefactor = ...
      * \prevgraf = ...
      * \deadcycles = ...
      * \insertpenalties = ...
    • special dimen assignment
      * \prevdepth = ...
      * \pagegoal = ...
      * \pagetotal = ...
      * \pagestretch = ...
      * \pagefilstretch = ...
      * \pagefillstretch = ...
      * \pagefilllstretch = ...
      * \pageshrink = ...
      * \pagedepth = ...

Local Assignments

The following is an exhaustive list of all of TeX's local assignments. (pp. 275-277 & 271) Unless preceded by \global, the effects of these assignments are confined to the current (most inner) grouping.

Macro assignments

  • \def ...
  • \edef ...

Non-macro assignments

  • variable assignment
    • integer assignment
      * <integer parameter> = ... (see below for a complete list of int. parameters)
      * <countdef token> = ... (<countdef token> = a c-seq. defined by \countdef)
      * \count<8-bit number> = ...
    • dimen assignment
      * <dimen parameter> = ... (see below for a complete list of dimen parameters)
      * <dimendef token> = ... (<dimendef token> = a c-seq. defined by \dimendef)
      * \dimen<8-bit number> = ...
    • glue assignment
      * <glue parameter> = ... (see below for a complete list of glue parameters)
      * <skipdef token> = ... (<skipdef token> = a c-seq. defined by \skipdef)
      * \skip<8-bit number> = ...
    • muglue assignment
      * <muglue parameter> = ... (see below for a complete list of muglue parameters)
      * <muskipdef token> = ... (<muskipdef token> = a c-seq. def. by \muskipdef)
      * \muskip<8-bit number> = ...
    • token assignment
      * <token parameter> = ... (see below for a complete list of token parameters)
      * <toksdef token> = ... (<toksdef token> = a c-seq. defined by \toksdef)
      * \toks<8-bit number> = ...
  • arithmetic
    • \advance ... (by) ...
    • \multiply ... (by) ...
    • \divide ... (by) ...
  • code assignment
    • \catcode<8-bit number> = ...
    • \mathcode<8-bit number> = ...
    • \lccode<8-bit number> = ...
    • \uccode<8-bit number> = ...
    • \sfcode<8-bit number> = ...
    • \delcode<8-bit number> = ...
  • let assignment
    • \futurelet ...
    • \let ... = ...
  • shorthand definition
    • \chardef ... = ...
    • \mathchardef ... = ...
    • \countdef ... = ...
    • \dimendef ... = ...
    • \skipdef ... = ...
    • \muskipdef ... = ...
    • \toksdef ... = ...
  • fontdef token assignment
    • <fontdef token> (<fontdef token> = a control-sequence defined by \font)
    • \nullfont
  • family assignment
    • \textfont<4-bit number> = ...
    • \scriptfont<4-bit number> = ...
    • \scriptscriptfont<4-bit number> = ...
  • \parshape = ...
  • \read ... to ...
  • \font ... = ... (at/scaled ...)
  • \setbox<8-bit number> = ...

TeX's 103 Parameters

The following is an exhaustive list of all of TeX's parameters.

Integer parameters (55)

\adjdemerits \binoppenalty \brokenpenalty \clubpenalty \day \defaulthyphenchar \defaultskewchar \delimiterfactor \displaywidowpenalty \doublehyphendemerits \endlinechar \errorcontextlines \escapechar \exhyphenpenalty \fam \finalhyphendemerits \floatingpenalty \globaldefs \hangafter \hbadness \holdinginserts \hyphenpenalty \interlinepenalty \language \lefthyphenmin \linepenalty \looseness \mag \maxdeadcycles \month \newlinechar \outputpenalty \pausing \postdisplaypenalty \predisplaypenalty \pretolerance \relpenalty \righthyphenmin \showboxbreadth \showboxdepth \time \tolerance \tracingcommands \tracinglostchars \tracingmacros \tracingonline \tracingoutput \tracingpages \tracingparagraphs \tracingrestores \tracingstats \uchyph \vbadness \widowpenalty \year

(pp. 272-273)

Dimen parameters (21)

\boxmaxdepth \delimitershortfall \displayindent \displaywidth \emergencystretch \hangindent \hfuzz \hoffset \hsize \lineskiplimit \mathsurround \maxdepth \nulldelimiterspace \overfullrule \parindent \predisplaysize \scriptspace \splitmaxdepth \vfuzz \voffset \vsize

(p. 274)

Glue parameters (15)

\abovedisplayshortskip \abovedisplayskip \baselineskip \belowdisplayshortskip \belowdisplayskip \leftskip \lineskip \parfillskip \parskip \rightskip \spaceskip \splittopskip \tabskip \topskip \xspaceskip

(p. 274)

Muglue parameters (3)

\medmuskip \thickmuskip \thinmuskip

(p. 274)

Token parameters (9)

\errhelp \everycr \everydisplay \everyhbox \everyjob \everymath \everypar \everyvbox \output

(p. 275)

* TeX automatically inserts a begin-group symbol ‘{’ at the beginning and an end-group symbol ‘}’ at the end of the argument assigned to the \output parameter. (p. 253)


The Primitive Grouping Environments

In the following '{' stands for either a character token with catcode 1 or a control-sequence \let to such a character token. Similarly, '}' stands for either a character token with catcode 2 or a control-sequence/active character \let to such a character token.

The following is a comprehensive list of all primitive grouping environments besides the obvious \begingroup ... \endgroup and unadulterated {...}.

  • \halign<box specification>{<alignment material>}
    \valign<box specification>{<alignment material>}
    \noalign<filler>{<vertical mode material>}

    With \halign, respectively \valign, TeX enters a new level of grouping, represented by the '{' and '}', within which changes to \tabskip will be confined. The alignment material can also contain optional occurrences of '\noalign<filler>{<vertical mode material>}' between lines; this adds another level of grouping. TeX also enters an additional level of grouping when it works on each individual entry of the alignment. (p. 282, 285-286)

  • \insert<8-bit number><filler>{<vertical mode material>}
    \vadjust<filler>{<vertical mode material>}

    The '{' causes TEX to enter a new level of grouping ended by the matching '}'. (pp. 280-281)

  • \hbox<box specification>{<box material>}
    \vbox<box specification>{<box material>}
    \vtop<box specification>{<box material>}
    \vcenter<box specification>{<box material>}

    The '{' initiates a new level of grouping ended by the the matching '}'. (pp. 278, 290)

  • $<math mode material>$
    $$<math mode material>$$

    Upon encountering '$' or '$$' in horizontal mode, TeX enters a new level of grouping, and stays there until the matching '$', respectively '$$'. (pp. 287, 293)

    A {...} pair encountered in math mode (as in the other modes) signals a new level of grouping. (pp. 290, 291)

  • \eqno<math mode material>$
    \leqno<math mode material>$

    Upon reading \eqno, respectively \leqno, TeX enters a new level of grouping until the end of the math list (signaled by the closing '$'). (p. 293)

  • \left<delim><math mode material>\right<delim>

    With '\left' TeX begins a new group, which is terminated by '\right'. (p. 292)

3
  • 2
    I'm not sure how this is an answer to the question posed!
    – Joseph Wright
    Aug 29, 2017 at 14:58
  • @JosephWright: It answers question #1.
    – Evan Aad
    Aug 29, 2017 at 15:01
  • 2
    @JosephWright The question asked how come font assignments (\fontdimen, \hyphenchar, \skewchar), hyphenation assignments (\hyphenation, \patterns), and box size assignments (\ht, \dp, \wd) appear to be “global” and don't get saved and restored in groups. It also asked “when to use or avoid grouping”. The accepted answer by Yiannis, the answer by David, and the second half of egreg's answer mostly answered the latter, while (the first half of) egreg's answer and this one answer the former. Aug 29, 2017 at 20:45

You must log in to answer this question.

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