12

From TeX by Topic, p. 111:

For certain applications, however, it is inconvenient that some of the plain macros are outer, in particular macros such as \newskip. One remedy is to redefine them, without the `outer' option, which is done for instance in LaTeX, but cleverer tricks are possible.

What are examples of such tricks for using \outer macros where they are not usually allowed?

1
15

As egreg said, the standard way is with \csname: as long as there is no \outer token at the time of the definition, you are safe.

So another possibility is, in a group, make the macro non-outer, define your macro, and then reset everything:

\begingroup
  \let\newdimen\relax
  \gdef\makedimenandgivevalue#1#2{%
    \newdimen#1%
    #1=#2\relax}
\endgroup

The problem with this version is that if you need to pass the \outer macro as argument to an auxiliary, you can't. A handier version is to define a non-outer wrapper to the \outer macro, then you can use it freely:

\edef\mynewdimen{\noexpand\newdimen}
%
\def\makedimenandgivevalue#1#2{%
  \mynewdimen#1%
  #1=#2\relax
}
13

The standard trick is \csname:

\def\makedimenandgivevalue#1#2{%
  \csname newdimen\endcsname#1%
  #1=#2\relax
}

Or \noexpand:

\edef\myproclaim#1#2\endmyproclaim{\noexpand\proclaim #1. \ignorespaces #2\par}

\myproclaim{Theorem}
This is better syntax.
\endmyproclaim

\proclaim Theorem.
This is worse syntax.

\bye

enter image description here

9

The famous \cleartabs, \settabs and \+ macros from plain.tex show one such trick (TeXbook p. 354):

\let\+=\relax % in case this file (plain.tex) is being read in twice
\def\sett@b{\ifx\next\+ \let\next=\relax % turn off \outerness
    \def\next{\afterassignment\s@tt@b\let\next}%
  \else\let\next=\s@tcols\fi\next}

...

\outer\def\+{\tabalign}
  1. \let\+=\relax is done before defining \sett@b in case \+ would already be an \outer token (otherwise, the \+ token in the replacement text of \sett@b would cause an error when \sett@b is defined).

  2. When the \ifx\next\+ test is true during normal usage of \settabs in the document, \next is an \outer token since in normal usage, \+ is \outer. Thus, Knuth does \let\next=\relax before redefining \next with \def\next{\afterassignment\s@tt@b\let\next}, otherwise the \next token at the end of the replacement text in this definition would cause an error.

4
  • +1 I love the irony ... – wave Mar 20 '20 at 6:22
  • Regarding “famous”? Well, these macros are famous for being the most difficult to understand in plain.tex. :) – frougon Mar 20 '20 at 8:19
  • 1
    no, just the fact that Knuth himself had to get rid of his own \outer definition due to the inconveniences coming along with it :D – wave Mar 20 '20 at 8:30
  • Ah, that way. :-) (Security is almost always antagonist to convenience, you know... for some special TeXish definition of security here) – frougon Mar 20 '20 at 8:35
8

For luatex, there is

\suppressoutererror = 1

which makes the entire issue go away.

You can use it globally or just locally in a group while you make the required definitions.

5

As already pointed out in egreg's answer:

If \outer-tokens are "hit" by \noexpand, they will be turned into \relax (only) for the next expansion:

\documentclass{article}

\outer\def\myoutermacro{My outer macro's definition.}

\def\FirstOfOne#1{#1}%

\newtoks\myscratchtoks

\begin{document}

% This does not work:
%
% \FirstOfOne{\myoutermacro}.
%
% These do work:

\expandafter\FirstOfOne\expandafter{\noexpand\myoutermacro}

\myscratchtoks\expandafter{\noexpand\myoutermacro}
\FirstOfOne{\the\myscratchtoks}

\edef\myscratchmacro{\noexpand\myoutermacro}
\FirstOfOne{\myscratchmacro}

\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.