Why can't the end code of an environment contain an argument?

\newenvironment{foo}[1]%
  {...} 
  {...}

this is internally defined as

\def\foo#1{....}
\def\endfoo{..}

The end part has by definition no argument.

\newenvironment{foo}[1]%
  {\def\fooNoI{#1}some code #1}
  {some code \fooNoI}

A workaround is to store information in a macro that is then accessible at the end of the environment. A key-value option parser may come in handy for this. In addition to graphics, PGF also provides a pretty good key-value system that can be used independently with the pgfkeys package.

\documentclass{minimal}
\usepackage{pgfkeys}

\newif\ifbar
\pgfkeys{/mypkg/.is family, /mypkg,
  default/.style = {bar=true, foo={not set!}}, % Initialize so 'defaults' exist
  foo/.store in = \foo,   % Any value assigned to foo will be stored in \foo
  bar/.is if = bar,       % Declare a boolean, defaults to false
}

\newenvironment{fooenv}[1][]{% Argument is optional as defaults were declared
  \pgfkeys{/mypkg/.cd, default, #1}%  Shift prefix to `mypkg` and parse arguments
}{%
 \textbf{\foo}~
 \ifbar
    Bar was true!
 \else 
    Bar was false!
 \fi
}

\begin{document}

  \begin{fooenv}[bar=false, foo={Hello, world!}]
    texting...
  \end{fooenv}

  \begin{fooenv}
    Blah blah.
  \end{fooenv}

\end{document}

This gives the following output:

texting... Hello, world! Bar was false!

Blah blah. not set! Bar was true!

pgfkeys is capable of much more and can be used with plain TeX, LaTeX or ConTeXt. See the "Key Handlers" section of the pgf manual for more details.


The reason the end code can't make references to the arguments passed to the start code is that they are expanded separately. That is, if you have an environment myenv taking one argument and you write

\begin{myenv}{myarg}
  some text...
\end{myenv}

then LaTeX expands \begin{myenv}, passing it the single argument myarg, and pastes the result in front of "some text...". The document then proceeds as it will, with other environments possibly opening and closing (perhaps even other instances of myenv) before \end{myenv} is finally reached. When that happens, it gets expanded, but there is no way of knowing anymore what the argument to the original \begin{myenv} was. Thus, there is no way of passing it to the end code unless you chose to save its value.

It's worth examining why this is confusing compared to \newcommand. Both appear to work in the same way:

\newcommand{\mymacro}[1]{macro code with #1}
\newenvironment{myenv}[1]{start code with #1}{end code}

The difference is that a macro is a single thing, which is reflected in the notation: you write \mymacro with a backslash but myenv without, perhaps signifying that it is a higher-level abstraction. Indeed, \newenvironment creates a pair of macros \myenv and \endmyenv which function as described above.

The setup is designed to create the appearance of "blocking off" the document into chunks contained in various environments, but in fact, the unity of each environment is a bit illusory. LaTeX keeps track of the name of the environment it most recently entered, but at no time (barring clever tricks) does it ever "see" the entire environment at once, either forward (when starting) nor backwards (when ending).

Tags:

Environments