LATEX-L@LISTSERV.UNI-HEIDELBERG.DE

 Options: Use Forum View Use Monospaced Font Show Text Part by Default Show All Mail Headers Message: [<< First] [< Prev] [Next >] [Last >>] Topic: [<< First] [< Prev] [Next >] [Last >>] Author: [<< First] [< Prev] [Next >] [Last >>]

On Thu, 13 Jul 2006 00:44:06 +0200, Javier Múgica de Rivera

Hi Javier,

> There is a control secuence named \chk_if_exist_cs:N with the following
> definition:
>
> \def:Npn \chk_if_exist_cs:N #1 {
>   \if:w \cs_if_exist_p:N #1
>   \else:
>     \err_latex_bug:x{Command~ \token_to_string:N #1'~
>                      not~ yet~ defined!}
>   \fi:}
>
> i.e., if the control sequence already exists it does nothing, but \else
> an error is issued. The opposite to \chk_if_exist_cs:N is, amazingly,
> \chk_new_cs:N,
>
> \def:Npn \chk_new_cs:N #1{
>   \if_meaning:NN #1\c_undefined
>   \else:
>     \if_meaning:NN #1\scan_stop:
>     \else:
>       \err_latex_bug:x {Command~name~\token_to_string:N #1'~
>                         Current~meaning:~\token_to_meaning:N #1
>                       }
>     \fi:
>   \fi:
> %<*trace>
> some code
> %</trace>
> }
>
> \chk_if_exist_cs:N does the check via \cs_if_exist_p:N, which reverts
> \cs_if_free_p:N, which in turn checks that the cs is undefined and not
> literally \c_undefined. But \chk_new_cs:N does not!, and it is the one
> actually used to test for free commands when new-defining something, so
> that something like \def_new:Npn\c_undefined{whatever} would work, and
> it shouldn't.

It certainly shouldn't! Thanks for pointing this out. I have changed the
function \chk_new_cs:N to follow the logic of only allowing to define a
control sequence if it is either undefined, \relax, and also textually
different from \c_undefined and \scan_stop:.

> It also happens that all the tests purposed to see if a cs is free, they
> test for equality with both \c_undefined and \scan_stop: (\relax). The
> logical equivalence of a cs equal to \scan_stop: to a undefined cs is
> due to the fact that \expandafter\ifx\csname foo\endcsname creates the
> control sequence, and lets it equal to \relax, as we all know, and also
> it was common to undefine'' a cs by \let\foo\relax. Now we can
> undefine via \let\foo\c_undefined (the control sequence \c_undefined is
> the most tricky trick I've ever seen. It would have been nice if we had
> known it from the begining) and test for the inexistence of a cs with
> etex \ifdefined and \ifcsname without creating it, so there is no reason
> why to think about someting equal to \scan_stop: as a free cs.

Unfortunately it is not quite that simple. How can \def_new:Npn or any
other function for that matter know how the control sequence to be defined
has been passed on to it? Answer is it can't. It would be very impractical
if
\def_new:Npn \foo{}
and
\def_new:cpn {foo}
were not identical.

There is only one sure-fire way to make to prevent a \cs:w ... \cs_end:
operation from turning the control sequence into \scan_stop: and that is
by introducing grouping which can then restore the meaning to undefined
after ending the group. For example you could do

\def:Npn \exp_args:Nc #1#2{
{\exp_after:NN } \exp_after:NN#1  \cs:w #2\cs_end:
}

This of course renders the whole operation non-expandable and so we are
faced with choosing the lesser of two evils. This is also why IMO the
\ifdefined primitive is of limited use because the token may very well
have been created by \cs:w ... \cs_end: at an earlier step in the
processing so you always have to do an additional check for \relax. With
the expansion mechanism used in the expl3 code base this is particularly
important.

On the other hand the \ifcsname primitive can be used in situations where
you know the control sequence in question is guaranteed to not have been
defined by accident elsewhere. An example from l3xref, where the property
list storing all information for a label is never entered directly as a
single token so not reason to create an extra entry in the hash table:

\def_new:Npn \xref_get_value:nn #1#2 {
\cs_if_really_free:cTF{g_xref_#2_plist}
{??}
{
%    \end{macrocode}
% This next expansion may look a little weird but it isn't if you
%    \begin{macrocode}
\exp_args:Ncc \exp_after:NN
{xref_get_value_#1_aux:w}{g_xref_#2_plist}
\q_nil
}
}

> There exist \if_really_free... that do these tests. In my opinion these
> should be the normal \cs_if_free..., and nothing with the extrange names
> \if_really_free... should exist. So my point of view is:
> 1. All tests that finally use tex \ifx for free-cs testing should be
> replaced by \if_cs_exist:N and \if_cs_exist:w (\ifdefined and \ifcsname)
> 2. A control sequence equal to \relax being equivalent to a free control
> sequence is an accident of the past we should commpletely forget about.

I disagree on both points for the reasons given above. I'm well aware that
the "really_free" name is bad but I couldn't think of anything better at
the time... I'm open to suggestions! :-)

> p.s. I also thik that names for testing if a certain condition is true
> or false should always contain if'' to avoid confusion.

I think I wrote a similar comment in the sources somewhere a while ago.
Anything that tests a certain condition should probably have a name
containing "if".
--
Morten