Environments are allowed to have arguments, but reference to those arguments can only appear in the opening code of the environment, and not the closing code. What is the reason for this, and are there preferred ways around it?

With the (LaTeX3) xparse package, you have \NewDocumentEnvironment where it's possible to refer to the arguments also in the closing code.
–
egregApr 30 '11 at 19:59

Could you say how that works? Does \NewDocumentEnvironment cause the start code to create a unique macro name, known to the end code, storing the arguments? Or does it look ahead? Some kind of call stack, perhaps?
–
Ryan ReichApr 30 '11 at 20:06

3

@Ryan: 'Neither of the above'. The approach is to store the arguments in a macro, then use it at the end of the environment. As LaTeX environments form groups, it's quite possible to arrange for this to work nicely. (I know because I wrote the current implementation, although the concept is not mine.)
–
Joseph Wright♦Apr 30 '11 at 20:36

@Joseph: you mean there's a macro like \envargs which gets filled each time an environment is called with arguments? So then TeX itself takes care of the "call stack" by saving and restoring its value when passing through nested environments.
–
Ryan ReichApr 30 '11 at 20:44

@Ryan: More or less, although to get everything right takes a little more effort.
–
Joseph Wright♦Apr 30 '11 at 21:25

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.

This is good advice. In fact, key-value parsers are a far superior way of dealing with arguments to high-level functions than the default, since they have meaningful semantics. For example, who can remember which optional argument to the \newtheorem command in amsthm subordinates the counter to another, and which one ties it to another?
–
Ryan ReichApr 30 '11 at 20:00

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:

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).

It's worth noting that this functions a lot differently than the environments defined by \newenvironment, since it absorbs the entire text of the minipage before processing any of it. This breaks \verbatim, for example, though also makes the minipage reusable and protects (for the particularly paranoid) against weird catcode changes.
–
Ryan ReichApr 30 '11 at 20:03

@Ryan Yes for sure it functions differently, but I just wanted to demonstrate some alternatives. As you correctly pointed out in your post in a LaTeX environment, LaTeX just prepends and appends code to some text that the user types in between.
–
Yiannis LazaridesApr 30 '11 at 20:11

I was not cricticizing, and I like this construction as well. However, this question seems intended as a FAQ, and so I wanted to add a bit of detail.
–
Ryan ReichApr 30 '11 at 20:16

will work as expected. The number of arguments must be specified by adding as many m tokens as wanted. So two arguments will require {mm}. This is because other argument types can be specified (optional arguments in several positions). Consult the package documentation for more details.