TeX - LaTeX Asked by Nat Kuhn on October 30, 2020
I was surprised to discover that when I create an environment in LaTex, a command of the same name is created. For example:
documentclass{minimal}
newenvironment{foo}{Foo: }{}
begin{document}
foo{bar}
end{document}
produces the output "Foo: bar" as you can see in this Overleaf example. In addition to Overleaf, it also works in Texpad, so it seems widespread.
My first question is: where can I find this documented? It doesn’t pop out in any of the places I usually look.
Furthermore, it doesn’t seem to be widely known. For example, when you use newcommand
to define a command, it appears as suggestion in code-completion. But when you use newenvironment
, it only appears in code-completion suggestions for begin{
, not for (i.e., not as a command).
It seems fantastically useful: I’d love to use align*{...}
rather than begin{align*}
/end{align*}
Second question: am I missing something? Is there some reason that this is problematic?
Thanks!
Edit: Thanks for the thoughtful detailed answers! To boil it down, the newenvironment
command creates two commands: in this case foo
and endfoo
which, generally speaking, contain your specified code. When you do begin{foo}
and end{foo}
, it not only surrounds your code with those commands, it also surrounds it with begingroup
and endgroup
, similar to wrapping the whole thing in braces. I have edited my overleaf example above for and MWE of how this fails with a user-defined command. I also found this answer helpful.
It's not fantastically useful, sorry. Try
documentclass{article}
begin{document}
This is some text flush left apart from indentation
center
This should be centered
endcenter
and this shouldn't be
end{document}
and you'll get the following output. You might be surprised, but you shouldn't be.
What happens? Certainly, doing newenvironment{foo}{...}{...}
defines foo
and endfoo
, because TeX only knows macros.
However, begin{foo}
is not the same as foo
and end{foo}
is not the same as endfoo
. Indeed, if we look at the definition of begin
, we see
% latex.ltx, line 7211:
DeclareRobustCommand*begin[1]{%
UseHook{env/#1/before}%
@ifundefined{#1}%
{defreserved@a{@latex@error{Environment #1 undefined}@eha}}%
{defreserved@a{def@currenvir{#1}%
edef@currenvline{on@line}%
@execute@begin@hook{#1}%
csname #1endcsname}}%
@ignorefalse
begingroup@endpefalsereserved@a}
and we realize that csname #1endcsname
, which in the case of begin{foo}
will become foo
, comes rather late in the processing. If we follow the “false” branch, the one that's used when the environment is indeed defined, we eventually get
@ignorefalsebegingroup@endpefalse
def@currenvir{foo}%
edef@currenvline{<some line number>}%
@execute@begin@hook{foo}%
foo
If you just use foo
, you miss all the preceding code. It's not important that you understand the code, but it's essential that you realize that it's there!
There is similar bookkeeping when end{foo}
is processed, in particular endgroup
is emitted to balance the previous begingroup
that you see in the code above.
Believe it or not, it is this endgroup
that makes a big difference in the center
example I showed.
By the way, amsmath
environments such as align
behave even differently and calling align...endalign
will break so many things!
There is a case when foo
and endfoo
can be used quite safely, for instance for defining a new environment based on foo
. But leave this to when you'll be an expert of LaTeX coding. Meanwhile, use begin{foo}...end{foo}
and be happy.
These are the errors I get if I add usepackage{amsmath}
and try
align a&=b endalign
Here they are:
Runaway argument?
a&=b endalign
! Paragraph ended before document was complete.
<to be read again>
par
l.7
?
! Missing $ inserted.
<inserted text>
$
l.7
?
! Missing endgroup inserted.
<inserted text>
endgroup
l.7
?
! Display math should end with $$.
<to be read again>
par
l.7
?
No output whatsoever. Not for the faint of heart.
Correct answer by egreg on October 30, 2020
Converting my comment into an answer.
There are important differences between macros and environments:
environments are group surrounded, not so with macros.
a macro tokenizes its argument at the outset, so nothing that happens inside the macro can affect, for example, the catcodes of the tokens in the argument. In the environment, tokens from the input stream are absorbed on the fly, subject to changes that have transpired in the course of the environment.
environments allow trailing code to be executed, once the input stream is exhausted (and the trailing code is needed to close out the group opened by environment).
The MWE below demonstrates all three of these differences.
The tokcycle
package allows one to cycle through the tokens of an argument or input stream and process them according to specified directives. The package provides both macro and pseudo-environment forms. By "pseudo-environment", I mean an environment requiring the use of macro...endmacro
syntax, rather than the more familiar begin{envname}...end{envname}
syntax.
In the MWE, I directly typeset (rather than store in a token register) the tokcycle
-processed input. The processing is as follows: any token will be echoed to the output, except cat-7 ^
tokens, which will be output as an fbox
ed string. When the process is complete, the value defined by aftertokcycle
is typeset, here being pre-set to an exclamation point !
I employ this processing using both macro and environment approaches to the following input: chcat This is a ^ test
, where chcat
is a macro that changes the catcode of ^
to a value of 12
.
Item 1 is demonstrated by showing that, following the macro exit, the catcode of ^
remains at 12
, whereas following the environment invocation, it returns (because of grouping) to its prior value of 7
.
Item 2 is demonstrated by noting that only the ^
in the macro gets fbox
ed. This is because, as part of a macro argument, the ^
is tokenized as catcode 7
, regardless of what changes transpire in the course of executing the argument. In the environment alternative, the ^
is not boxed, because it has been tokenized only after the catcode of ^
has been changed to 12
in the course of executing the input stream.
Item 3 is demonstrated by the absence of the trailing !
in the environment version. Why? Because the environment form executes its own trailing code through the same macro employed by the aftertokcycle
invocation. Thus, the prior invocation of aftertokcycle
carries no sway over the environmental form, which uses its trailing code to redefine that variable. The macro form does not execute any trailing code, so a predefined aftertokcycle
still holds sway.
documentclass{article}
usepackage{tokcycle}
defchcat{catcode`^=12}
begin{document}
Characterdirective{tctestifcatnx^#1{fbox{string#1}}{#1}}
Groupdirective{processtoks{#1}}
Macrodirective{#1}
Spacedirective{#1}
aftertokcycle{!}
Macro form:
begingroup
tokcyclexpress{chcat This is a ^ test}
Caret catcode: numbercatcode`^
endgroup
Environment form:
tokencyclexpress chcat This is a ^ testendtokencyclexpress
Caret catcode: numbercatcode`^
end{document}
For the geek: The macro and environment forms of tokcyle
used in this MWE both rely on the same underlying "raw" pseudoenvironment. The code to these interface forms may help to clarify why the macro acts like a macro and the pseudo-environment acts like an environment:
Macro Form (xpress interface):
% XPRESS-INTERFACE MACRO FORM
longdeftokcyclexpress#1{tokcycrawxpress#1endtokcycraw}
Pseudo-environment form (xpress interface):
% XPRESS-INTERFACE ENVIRONMENT FORM
deftokencyclexpress{begingroupletendtokencyclexpressendtokcycraw
aftertokcycle{thecytoksexpandafterendgroupexpandaftertcenvscope
expandafter{thecytoks}}tokcycrawxpress}
Answered by Steven B. Segletes on October 30, 2020
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP