Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Implementing ifundefined under Plain TeX

133 views
Skip to first unread message

car...@colorado.edu

unread,
Apr 22, 2011, 2:24:38 PM4/22/11
to
Hi - for a citation macro I would like to implement \ifundefined under
Plain TeX
with the standard TeX engine. I tried to parrot LaTeX's @ifundefined
as shown
below, and seems to work. Any foreseeable problems with this
particular code?
Thanks.

%LaTex implementation:
%\def\@ifundefined#1{%
% \expandafter\ifx\csname#1\endcsname\relax
% \expandafter\@firstoftwo
% \else
% \expandafter\@secondoftwo
% \fi}

\parskip=10pt \def\AAAA{1}

\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}

\def\ifundefined#1{%
\expandafter\ifx\csname#1\endcsname\relax
\expandafter\firstoftwo
\else
\expandafter\secondoftwo
\fi}

\ifundefined{AAAA}{AAAA is undefined}{AAAA is defined} \par
\ifundefined{BBBB}{BBBB is undefined}{BBBB is defined}

\bye

Running gives

AAAA is defined

BBBB is undefined

Heiko Oberdiek

unread,
Apr 22, 2011, 2:45:31 PM4/22/11
to
car...@colorado.edu wrote:

> Hi - for a citation macro I would like to implement \ifundefined under
> Plain TeX
> with the standard TeX engine.

See package ltxcmds.sty, it can also be used with plain TeX
or even iniTeX. It implements
\ltx@IfUndefined and \ltx@ifundefined
in different ways, depending on the TeX engine
(e.g. eTeX provides \ifcsname).

> I tried to parrot LaTeX's @ifundefined
> as shown
> below, and seems to work. Any foreseeable problems with this
> particular code?
> Thanks.
>
> %LaTex implementation:
> %\def\@ifundefined#1{%
> % \expandafter\ifx\csname#1\endcsname\relax
> % \expandafter\@firstoftwo
> % \else
> % \expandafter\@secondoftwo
> % \fi}

Undefined commands become the meaning \relax.
This is a side effect of \csname.
That's undefined in the sence of \@ifundefined, but
not using
\ifx\mymacro\UNDEFINED
\message{undefined}%
\else
\message{defined}%
\fi
\@ifundefined{mymacro}{}{}
\ifx\mymacro\UNDEFINED
\message{undefined}%
\else
\message{defined}%
\fi

Is \mymacro is undefined in the first place,
then it's defined afterwards.

This can be fixed by using a group:

\def\@undefined@nonexpandable#1{%
\begingroup\expandafter\expandafter\expandafter\endgroup
expandafter\ifx\csname #1\endcsname\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}

But this version is not expandable.
If eTeX is available, then also an
expandable way can be provided,
see ltxcmds.

--
Heiko Oberdiek

car...@colorado.edu

unread,
Apr 22, 2011, 4:08:53 PM4/22/11
to
On Apr 22, 12:45 pm, Heiko Oberdiek <heiko.oberd...@googlemail.com>
wrote:

Thanks for the advice. Actually I didnt see those "name pollution"
side effects so far. If I do more tests,

\ifundefined{AAAA}{AAAA is undefined}{AAAA is defined} \par

\ifundefined{BBBB}{BBBB is undefined}{BBBB is defined} \par
\ifundefined{BBBB}{BBBB is undefined}{BBBB is defined} \par
\ifundefined{relax}{relax is undefined}{relax is defined} \par
\ifundefined{ifundefined}{ifundefined is undefined}{ifundefined is
defined} \par


\ifundefined{AAAA}{AAAA is undefined}{AAAA is defined} \par

\ifundefined{BBBB}{BBBB is undefined}{BBBB is defined} \par

I get the expected answers. Well, with one exception: \relax is
considered undefined - bit of a mistery to me since it is a primitive
defined to do nothing.

Are there more stringent tests that point the need
for extra grouping?


Donald Arseneau

unread,
Apr 22, 2011, 4:58:20 PM4/22/11
to
car...@colorado.edu writes:

> Hi - for a citation macro I would like to implement \ifundefined under Plain
> TeX with the standard TeX engine. I tried to parrot LaTeX's @ifundefined as
> shown below, and seems to work. Any foreseeable problems with this particular
> code? Thanks.

The problem is that making \csnames of things actually
defines the command as \relax, so you can't later perform
the test \ifx\foo\undefined. If you can run "etex" and
use \ifcsname then there is not that conflict.


Donald Arseneau as...@triumf.ca

car...@colorado.edu

unread,
Apr 22, 2011, 5:28:31 PM4/22/11
to
> Donald Arseneau                          a...@triumf.ca

That is a good warning. I'll check with \show\cs if there is
pollution,
and apply grouping to kill those names.

Philipp Stephani

unread,
Apr 22, 2011, 5:31:05 PM4/22/11
to
car...@colorado.edu writes:

> Thanks for the advice. Actually I didnt see those "name pollution"
> side effects so far.

Because your \ifundefined (like LaTeX's \@ifundefined) doesn't
distinguish between undefined and \relax.

> Well, with one exception: \relax is considered undefined - bit of a
> mistery to me

Please reread Heiko's answer. You actually test whether the control
sequences are equal to \relax, not whether they are undefined, and
because you use \csname, you define them while testing. Traditionally
control sequences equal to \relax and undefined control sequences have
not been distinguished by LaTeX, but technically they are different.

--
Change “LookInSig” to “tcalveu” to answer by mail.

GL

unread,
Apr 22, 2011, 5:39:54 PM4/22/11
to

No this is because of the behaviour of \csname that you have understood:
one usually consider that a cs is not defined if it's meaning is
\undefined or \relax.


You can use another code (simpler by the way):

\def\testifnotdefined #1{\begingroup
\expandafter \endgroup \expandafter \ifx
\csname #1\endcsname\@undefined


\expandafter\@firstoftwo
\else \expandafter\@secondoftwo \fi
}

\testifnotdefined {something}{not defined }{ defined}

\testifnotdefined {something}{not defined }{ defined}

\testifnotdefined {relax}{not defined }{ defined}

car...@colorado.edu

unread,
Apr 22, 2011, 6:01:30 PM4/22/11
to
On Apr 22, 3:39 pm, GL <gouail...@gmail.com> wrote:
> Le 22/04/2011 22:08, car...@colorado.edu a crit :

This seems to work fine too:

\def\ifundefined#1{%
\begingroup \expandafter\ifx\csname#1\endcsname\relax


\expandafter\firstoftwo
\else
\expandafter\secondoftwo

\fi\endgroup}

since \show\BBBB gives =\undefined at the end of testing. Should I
make
\firstoftwo and \secondoftwo \global just in case?

That's one thing Don overlooked. Should have made \undefined
an untouchable primitive, distinguishable from \relax. Also
added \undef and \undefall primitives. Well he wanted to get back to
writing his books ...

GL

unread,
Apr 22, 2011, 6:07:10 PM4/22/11
to
Le 23/04/2011 00:01, car...@colorado.edu a écrit :
> This seems to work fine too:
>
> \def\ifundefined#1{%
> \begingroup \expandafter\ifx\csname#1\endcsname\relax
> \expandafter\firstoftwo
> \else
> \expandafter\secondoftwo
> \fi\endgroup}

No there is a problem: \endgroup is misplaced:

\expandafter\@firstoftwo \else ...\fi \endgroup

becomes: \@firstoftwo \endgroup {....} => \endgroup

But do you want to test for undefined only or for undefined or relax ?

\begingroup \expandafter \ifx \csname #1\endcsname\relax
\endgroup \expandafter\@firstoftwo
\else \endgroup \expandafter\@secondoftwo
\fi

Ulrich D i e z

unread,
Apr 22, 2011, 6:20:21 PM4/22/11
to
<car...@colorado.edu> wrote:

> Are there more stringent tests that point the need
> for extra grouping?

\meaning delivers a sequence of catcode-12-character-tokens
denoting the phrase "undefined" in case the control-sequence/
active-character in question is undefined.

Crucial question: Can you trick \meaning into delivering
exactly a sequence of catcode-12-character-tokens
denoting the phrase "undefined" as leading sequence of its
result although the token in question is defined/has a
meaning?

If this is not possible, a check on the control-sequence-token
itself (not the sequence you'd pass to \csname..\endcsname)
might be feasible by testing whether the result of applying
\meaning to the control-sequence-token in question starts
with "undefined".

Ulrich


%%===============================================================
%%
%%---------------------------------------------------------------
%% \firstoftwo and \secondoftwo:
%%...............................................................


\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}

%%---------------------------------------------------------------
%% Check if control-sequence-token has been assigned a meaning:
%%...............................................................
\def\AtIfUndefined#1{%
\long\def\AtIfUndefined##1##2##3{%
\romannumeral\iffalse{\fi\expandafter\innerAtIfUndefined
\expandafter.\meaning##1#1}{}{0 ##3}{0 ##2}%
}%
\long\def\innerAtIfUndefined##1#1{%
\iffalse{\fi\expandafter\expandafter\expandafter\secondoftwo
\expandafter\expandafter\expandafter{\expandafter\expandafter
\expandafter{\expandafter\string\firstoftwo{}##1}\expandafter
\secondoftwo\string}\expandafter\firstoftwo\expandafter{%
\iffalse}\fi\expandafter\expandafter\expandafter\firstoftwo}%
{\expandafter\expandafter\expandafter\secondoftwo}%
\expandafter\secondoftwo\expandafter{\iffalse}\fi
}%
}%
% Somehow get the catcode-12-token-phrase "undefined" as argument
% of \AtIfUndefined:
\expandafter\AtIfUndefined\expandafter{\meaning\UndFINeD}%
%%===============================================================

\def\test{undefined}

\catcode`\Z=13

\AtIfUndefined{\UndFINeD}{undefined}{defined}

\AtIfUndefined{\relax}{undefined}{defined}

\AtIfUndefined{\TeX}{undefined}{defined}

\AtIfUndefined{\test}{undefined}{defined}

\AtIfUndefined{\par}{undefined}{defined}

\AtIfUndefined{\fi}{undefined}{defined}

\AtIfUndefined{\AtIfUndefined}{undefined}{defined}

\AtIfUndefined{\bgroup}{undefined}{defined}

\AtIfUndefined{\egroup}{undefined}{defined}

\AtIfUndefined{Z}{undefined}{defined}

\letZ=\relax

\AtIfUndefined{Z}{undefined}{defined}

\AtIfUndefined{\UndFINeD}{undefined}{defined}

\bye

GL

unread,
Apr 22, 2011, 6:29:54 PM4/22/11
to
Le 23/04/2011 00:20, Ulrich D i e z a écrit :

> <car...@colorado.edu> wrote:
> %% Check if control-sequence-token has been assigned a meaning:
> %%...............................................................
> \def\AtIfUndefined#1{%
> \long\def\AtIfUndefined##1##2##3{%
> \romannumeral\iffalse{\fi\expandafter\innerAtIfUndefined
> \expandafter.\meaning##1#1}{}{0 ##3}{0 ##2}%
> }%
> \long\def\innerAtIfUndefined##1#1{%
> \iffalse{\fi\expandafter\expandafter\expandafter\secondoftwo
> \expandafter\expandafter\expandafter{\expandafter\expandafter
> \expandafter{\expandafter\string\firstoftwo{}##1}\expandafter
> \secondoftwo\string}\expandafter\firstoftwo\expandafter{%
> \iffalse}\fi\expandafter\expandafter\expandafter\firstoftwo}%
> {\expandafter\expandafter\expandafter\secondoftwo}%
> \expandafter\secondoftwo\expandafter{\iffalse}\fi
> }%
> }%
> % Somehow get the catcode-12-token-phrase "undefined" as argument
> % of \AtIfUndefined:
> \expandafter\AtIfUndefined\expandafter{\meaning\UndFINeD}%

Impressive... but: is there a link with typography ?

Message has been deleted

Ulrich D i e z

unread,
Apr 22, 2011, 6:59:08 PM4/22/11
to
GL wrote:

> Impressive... but: is there a link with typography ?

The OP did not ask about "a link with typography" but
about implementing \ifundefined under Plain TeX.

Many discussions on that matter took place already.

Thus I decided to take this discussion for a moot
point.

I seldom use TeX for accomplishing tasks related to
typography. It's a toy similar to a crossword puzzle
or a chessboard.

Ulrich

Ulrich D i e z

unread,
Apr 22, 2011, 7:33:42 PM4/22/11
to
[Supersedes: news:iot1cb$o9l$1...@news.albasani.net ]

In my previous posting I overlooked that with \meaning you
don't need to take extra care for preventing brace-stripping
from arguments as no tokens of catcode1/2 will be involved.

I don't know what I was thinking but I assume that there is
still the possibility of optimization.

Ulrich


%%===============================================================


%% \firstoftwo and \secondoftwo:
%%...............................................................
\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}
%%---------------------------------------------------------------
%% Check if control-sequence-token has been assigned a meaning:
%%...............................................................
\def\AtIfUndefined#1{%
\long\def\AtIfUndefined##1##2##3{%
\romannumeral\iffalse{\fi\expandafter\innerAtIfUndefined

\meaning##1#1}{}{0 ##3}{0 ##2}%
}%
\long\def\innerAtIfUndefined##1#1{%

\iffalse{\fi\expandafter\secondoftwo\expandafter{\string##1}%
\expandafter\firstoftwo\expandafter{\iffalse}\fi\expandafter
\expandafter\expandafter\firstoftwo}{\expandafter\expandafter
\expandafter\secondoftwo}\expandafter\secondoftwo\expandafter


{\iffalse}\fi
}%
}%
% Somehow get the catcode-12-token-phrase "undefined" as argument
% of \AtIfUndefined:
\expandafter\AtIfUndefined\expandafter{\meaning\UndFINeD}%
%%===============================================================

\def\test{undefined}

\def\testb{{{braces}}}

\catcode`\Z=13

\AtIfUndefined{\UndFINeD}{undefined}{defined}

\AtIfUndefined{\relax}{undefined}{defined}

\AtIfUndefined{\TeX}{undefined}{defined}

\AtIfUndefined{\test}{undefined}{defined}

\AtIfUndefined{\par}{undefined}{defined}

\AtIfUndefined{\fi}{undefined}{defined}

\AtIfUndefined{\AtIfUndefined}{undefined}{defined}

\AtIfUndefined{\bgroup}{undefined}{defined}

\AtIfUndefined{\egroup}{undefined}{defined}

\AtIfUndefined{Z}{undefined}{defined}

\letZ=\relax

\AtIfUndefined{Z}{undefined}{defined}

\AtIfUndefined{\testb}{undefined}{defined}

Donald Arseneau

unread,
Apr 24, 2011, 2:42:55 AM4/24/11
to
Ulrich D i e z <eu_an...@web.de> writes:

> \meaning delivers a sequence of catcode-12-character-tokens..


> %% Check if control-sequence-token has been assigned a meaning:

I don't see the point. If you are testing a control sequence token
then just use \ifx. The problem is with copying LaTeX's \@ifundefined
where the cs name is given as a text argument; \csname \endcsname has
unfortunate properties (confusion between undefined and relax) and side
effects (assignment as \relax). These deficiences are addressed by
e-TeX with \ifcsname.


Donald Arseneau as...@triumf.ca

Robin Fairbairns

unread,
Apr 24, 2011, 5:14:13 AM4/24/11
to
Donald Arseneau <as...@triumf.ca> writes:

but the op didn't want to do anything so radical as to use something don
k does without. i've never understood this attitude, but there we are.
--
Robin Fairbairns, Cambridge
my address is @cl.cam.ac.uk, regardless of the header. sorry about that.

Ulrich D i e z

unread,
Apr 24, 2011, 5:55:19 AM4/24/11
to
Donald Arseneau wrote:

> Ulrich D i e z <eu_an...@web.de> writes:
>
> > \meaning delivers a sequence of catcode-12-character-tokens..
> > %% Check if control-sequence-token has been assigned a meaning:
>
> I don't see the point. If you are testing a control sequence token
> then just use \ifx.

You mean comparing the token in question to an undefined
control-sequence? Of course. As I experienced that some
people somehow managed to get \undefined defined, I usually
rely on some control-sequence with a rather obscure name,
e.g., \WEirDANdtHUshOPeFULlYUndeFIned, being undefined.

But in
news:296fcd15-cf09-4658...@22g2000prx.googlegroups.com
, Carlos wrote:

| That's one thing Don overlooked. Should have made \undefined
| an untouchable primitive, distinguishable from \relax. Also
| added \undef and \undefall primitives. Well he wanted to get back to
| writing his books ...

Due to the phrase "untouchable primitive" it seemed to me
that Carlos did not feel all too comfortable about relying on
a certain control-sequence being undefined.

Thus I presented the only way I know of expandably testing
whether a control-sequence is (un)defined in plain TeX without
the need of all the time relying on a certain token not being
defined.

Of course with that approach you instead need to rely on
other tokens not being redefined which might as well not
be the case with code written by people who define tokens
like "\undefined".

> \csname \endcsname has unfortunate properties

The problem is that \csname..\endcsname not only forms
a control-sequence-token but also makes that control-
sequence-token equal to \relax in the current scope in case
that control-sequence-token is not defined yet in the current
scope. (Seems that even if the value of the integer-parameter
\globaldefs is positive, the effect is restricted to the current
scope.)
This effect affects the result of testing. If you don't want
to use eTeX extensions but plain TeX while facing the need
of first "constructing" the control-sequence-token from a
sequence of characters, you need to choose whether to
cancel that effect by grouping (which implies that the test
won't be expandable any more) or whether actually rather
comparing the newly constructed control-sequence-token to
(the result of something that delivers a "frozen") \relax than
checking whether the control-sequence-token in question is
defined at all.

Sincerely

Ulrich

Ulrich D i e z

unread,
Apr 24, 2011, 6:10:45 AM4/24/11
to
I wrote:

> Donald Arseneau wrote:
>
> > Ulrich D i e z <eu_an...@web.de> writes:
> >
> > > \meaning delivers a sequence of catcode-12-character-tokens..
> > > %% Check if control-sequence-token has been assigned a meaning:
> >
> > I don't see the point. If you are testing a control sequence token
> > then just use \ifx.
>

> You mean [...]

Reconsidering the facts I come to the conclusion that
once more actually the point is that I was carried away
in a weird fashion when scenting the opportunity of
showing-off.

I ask the gentle reader's forgiveness.

Sincerely

Ulrich

Donald Arseneau

unread,
Apr 24, 2011, 1:20:19 PM4/24/11
to
Ulrich D i e z <eu_an...@web.de> writes:

> Reconsidering the facts I come to the conclusion that
> once more actually the point is that I was carried away

Don't worry, there's no accusation of grandstanding. Just
normal topic-drift.

Donald Arseneau as...@triumf.ca

Message has been deleted
0 new messages