## LATEX-L@LISTSERV.UNI-HEIDELBERG.DE

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

 Re: Ifs Morten Høgholm <[log in to unmask]> Tue, 18 Jul 2006 00:31:53 +0200 text/plain (126 lines) On Thu, 13 Jul 2006 00:44:06 +0200, Javier Múgica de Rivera <[log in to unmask]> wrote: 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'~ > already~defined!~ > Current~meaning:~\token_to_meaning:N #1 > } > \fi: > \fi: > %<*trace> > some code > % > } > > \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 % think about it! % \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