A beamer buggy handling of newcommand?

Does anyone know why I get error here? Analogous code works for document class "slides" in latex.

\documentclass[compress]{beamer}
\newcommand{\sld}{\end{frame} \begin{frame}}
\begin{document}
\begin{frame}
Test -- Page 1
\sld
Test -- Page 2
\end{frame}
\end{document}


migrated from stackoverflow.comJan 17 '14 at 8:19

This question came from our site for professional and enthusiast programmers.

beamer's frame environment is very complicated, since it has to deal with the overlay specification - something inherent to beamer that the end-user rarely have to worry about. That is, you set what looks like a single frame environment, while beamer translates it into a number of slides (possibly).

beamer also reads the entire frame environment in order to manage multiple instances of it and technically misses your \sld's \end{frame}. Other document classes have other ways of managing this, since they don't provide/lack beamer overlay specification, and as such only processes frames once.

Here is a more lengthy and detailed discussion:

This procedure of taking "one thing" (a single frame environment) and replicating it into "multiple things" (numerous slides) requires beamer to grab the entire frame environment before starting to process it. We can see this "frame grabbing" when you look at certain components inside beamerbaseframe.sty:

%
% The frame environment (a trifle ugly...)
%

% Copyright notice: the following code is adapted from code from the
% amsmath package.

\newtoks\beamer@envbody
\def\beamer@frameenv{%
\def\beamer@process@envbody{\endgroup%
\expandafter\expandafter\expandafter\beamer@framecommand\expandafter\beamer@frameoptions\expandafter{\the\beamer@envbody}}%
\global\beamer@envbody{}\def\beamer@begin@stack{b}%
\begingroup
\let\frame\beamer@collect@@body
\def\beamer@process@envbody{\frame}%
\beamer@process@envbody%
}

% Normally not executed; only in containsverbatim context:
\def\endframe{\egroup\begingroup\def\@currenvir{frame}}

\long\def\beamer@push@begins#1\begin#2{\ifx\end#2\else b\expandafter\beamer@push@begins\fi}
\long\def\beamer@collect@@body#1\end#2{%
\def\test{#1}%
\edef\beamer@begin@stack{\beamer@push@begins#1\begin\end \expandafter\@gobble\beamer@begin@stack}%
\ifx\@empty\beamer@begin@stack
\global\beamer@envbody\expandafter{\the\beamer@envbody#1}%
\def\reserved@a{#2}
\ifx\reserved@a\beamer@frametext%
\endgroup%
\let\@next=\beamer@process@envbody % A little tricky! Note the grouping
\@checkend{#2}%
\else%
% ok, just expand it, presumably it will generate the desired \end{frame}
\let\@next=\beamer@process@expander%
\fi
\else
\global\beamer@envbody\expandafter{\the\beamer@envbody#1\end{#2}}%
\let\@next=\beamer@process@envbody%
\fi
\@next
}


Note how \beamer@frameenv - fundamentally, the start of the frame environment, calls \beamer@process@envbody, which expands to \frame, which expands to \beamer@collect@@body. Further down we see that \beamer@collect@@body's parameter text is defined to include \end. More specifically,

\long\def\beamer@collect@@body#1\end#2{...}


This means that the macro grabs everything from the start of the environment up until \end into argument #1, and the environment name as the argument to \end in argument #2. At the stage of grabbing these arguments (not only are the category codes fixed, but also) TeX has to see a visible \end.

In your case, the only visible \end is that of the "second" frame. As such, what seems like two frames in your mind is considered a single frame in TeX's mind. And, with an odd \end{frame} (the expansion of \sld) that doesn't have an accompanying \begin{frame}, TeX complains.

• Note that you see the same inability to 'hide' stuff inside macros with for example AMSmath's \begin{align}/\end{align} pair for a not dissimilar reason. – Joseph Wright Jan 17 '14 at 8:20
• Great explanation for a process that goes totally unadverted for the user and can cause a headache. – Jose Brox Mar 10 '15 at 15:30