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.

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?

e.g.

\newenvironment{foo}[1]%
{some code #1} % allowed
{some code #1} % not allowed
share|improve this question
6  
With the (LaTeX3) xparse package, you have \NewDocumentEnvironment where it's possible to refer to the arguments also in the closing code. –  egreg Apr 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 Reich Apr 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 Reich Apr 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
add comment

7 Answers

up vote 35 down vote accepted
\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}
share|improve this answer
5  
I wouldn't use \gdef, the scratch macro \fooNoI will not be used any more and the \endfoo code will be executed before the \endgroup. –  egreg Apr 30 '11 at 19:37
add comment

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/foo/.store in=\foo,   % Any value assigned to foo will be stored in \foo
  /mypkg/foo=not set,          % Initialize so \foo exists
  /mypkg/bar/.is if=bar,       % Declare a boolean, defaults to false
}

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

\begin{document}

  \begin{fooenv}[bar=true,foo={Hello, world!}]
    Blah blah blah
  \end{fooenv}

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

\end{document}

This gives the following output:

Blah blah blah Hello, world!Bar was true!

Blah blah. not set

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.

share|improve this answer
    
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 Reich Apr 30 '11 at 20:00
add comment

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

share|improve this answer
add comment

Environments are simply a TeX technique, used extensively by Knuth to delimit parameters of a macro by other macros and defining two commands one for the starting macro and one for the ending macro.

To see that they are actually two macros, you can try this:

\minipage{30pt} one\endminipage

You can define your own environments easily (including pseudo-parameters) for the ending macro (they are actually parameters of the first) in a non-traditional manner as follows:

\documentclass{article}
\begin{document}
\long\def\minipage #1\endminipage#2{#1,#2}

\minipage one\endminipage{two}
\end{document}
share|improve this answer
    
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 Reich Apr 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 Lazarides Apr 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 Reich Apr 30 '11 at 20:16
add comment

My way of dealing with the problem:

\documentclass{article}

\newenvironment{myenv}[1]
{\newcommand{\foot}{#1}{\bf Beginning(#1)}: }
{{\bf The end (\foot)}}

\begin{document}

\begin{myenv}{Oki doki}
This is my environment, which is quite useless.
\end{myenv}

\end{document}
share|improve this answer
2  
Related tex.stackexchange.com/questions/15361/… –  percusse Oct 31 '12 at 0:27
add comment

Another possibility is using the environ package:

\documentclass{article}

\usepackage{environ}
\NewEnviron{foo}[1]{\BODY\ #1}

\begin{document}

\begin{foo}{works!} This\end{foo}

\end{document}
share|improve this answer
add comment

While Herbert and Ryan Reich answered the reason as why it is not possible, I'll give my solution to solve the problem which is similar to Mateusz Maciejewsk's answer:

\newcommand{\fooArg}{} % dummy macro
\newenvironment{foo}[1]{%
  \renewcommand{\fooArg}{#1}%
  some code #1 %
}{ %
  some code \fooArg %
}

It might not be usable in all situations, but it might solve the problem if you are in hurry or don't want to use specific package.

share|improve this answer
    
It seems identical rather than similar... –  percusse Aug 2 '13 at 22:34
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.