# A problem for LaTeX's \@ifnextchar

I wanted to construct a command, say \newlet, that I could use to do

\newlet\x\y


and/or

\newlet\x=\y


such that \x is unique (ie, not already in use).

Here we go

\protected\def\newlet#1{%
\@ifnextchar={\@firstoftwo{\@newlet{#1}}}{\@newlet{#1}}%
}
\def\@newlet#1{\@ifdefinable{#1}\relax\let#1= }

% Obviously works:
\newlet\sptoken=\@sptoken

% Obviously fails:
\newlet\sptoken\@sptoken

-
Could I ask what the idea is here? It's quite possible to define a function \newlet which will carry out a check and which works with implicit spaces, but the optional = is a pain. – Joseph Wright Feb 28 '12 at 19:39
This seems to be a limitation of \@ifnextchar. I really wanted to do \newlet\temp@sptoken\@sptoken and it failed. I then traced the problem to the fact that \@xifnch expects an explicit blank space. In essence can \@ifnextchar be modified to work in 'all' cases? – Ahmed Musa Feb 28 '12 at 19:51
@AhmedMusa You could use amsmath's \new@ifnextchar, but then \newlet\x= \y would not work. – egreg Feb 28 '12 at 20:44
@egreg: Thanks. I knew about Michael Downes' \new@ifnextchar in AMS packages, but as you have noted, \newlet\x= \y fails. – Ahmed Musa Feb 28 '12 at 21:38

Dealing with spaces in a general way is tricky. What you want here is a peek-ahead-and-remove function. This is available in LaTeX3 as \peek_meaning_remove:NTF, where I would use

\RequirePackage{expl3}
\ExplSyntaxOn
\cs_new_protected:Npn \newlet #1
{
\peek_meaning_remove:NTF =
{ \cs_new_eq:NN #1 }
{ \cs_new_eq:NN #1 }
}
\ExplSyntaxOff
\makeatletter
\newlet\sptoken=\@sptoken


This can of course be 'written out' in primitives. Ignoring the need to be careful about & tokens (using an Appendix D trick), something like

\makeatletter
\protected\long\def\newlet#1{%
\protected\def\@newlet@fcommon{%
\ifdefined#1%
\expandafter\ERROR
\fi
\let#1=
}%
\let\@tempa= =%
\futurelet\@let@token\@newlet
}
\protected\def\@newlet{%
\ifx\@let@token\@tempa
\expandafter\@newlet@true
\else
\expandafter\@newlet@fcommon
\fi
}
\protected\def\@newlet@true{%
\afterassignment\@newlet@fcommon
\let\@temp@= %
}


will work (it's a simplified version of \peek_meaning_remove:NTF with the branches hard-coded). Notice in particular that the = is removed using a \let along with an \afterassignment to regain control, rather than using a parameter.

The basic idea here is not dis-similar to \@ifnextchar, but does not include the loop to remove spaces which the latter does. The LaTeX3 bundle provides both 'pre-packaged' (the equivalent to \@ifnextchar is \peek_meaning_remove_spaces:NTF).

-
Thanks. But this fails for \def\z#1{\newlet\y#1}\z{ }=\z. – Ahmed Musa Feb 28 '12 at 21:22
@AhmedMusa I'm not sure what you mean. In your example, \y is \let to a space, and only then to you have an =. As that is after \z, it ends up being typeset. – Joseph Wright Feb 28 '12 at 21:36
I was referring to the need for space gobbling as LaTeX kernel's \@ifnextchar does. In \newlet\y<forced space>=\z, I would expect <forced space> to be gobbled first. Thanks. I will try a scheme by Donald Arseneau that I saw applied in a different context. – Ahmed Musa Feb 29 '12 at 19:18

Here is a variation on Joseph Wright's idea:

\protected\long\def\newlet#1{%
\def\@newlet##1{%
\ifdefined#1%
\fi
\let#1=
}%
\futurelet\@let@token\@@newlet
}
\protected\def\@@newlet{%
\ifx\@let@token=%
\expandafter\@newlet
\else
\expandafter\@newlet\expandafter\relax
\fi
}


But it still fails for

\def\z#1{\newlet\y#1}\z{ }=\z
`
-