Please consider the following MWE, which is an excerpt from a much larger project.
The scenario is to define a conditional \IfComp{<number>}{<defined>}{<undefined>} that queries, whether or not a final control sequence (in this case \csname test-<number>\endcsname) is defined via an intermediary control sequence (\csname macro-<number>\endcsname).
\documentclass{minimal}
\makeatletter
\parindent\z@
\expandafter\def\csname test-1\endcsname{foo}
\expandafter\def\csname macro-1\endcsname{\csname test-1\endcsname}
\expandafter\def\csname macro-2\endcsname{\csname test-2\endcsname}
\def\testA{%
\long\def\IfComp##1##2##3{%
\expandafter\expandafter\expandafter\let\expandafter\csname @comp@name\expandafter\endcsname\csname macro-##1\endcsname%
\expandafter\expandafter\expandafter\ifx@comp@name\relax
##3
\else
##2
\fi%
}}
\def\testB{%
\long\def\IfComp##1##2##3{%
\expandafter\expandafter\expandafter\ifx\csname macro-##1\endcsname\relax
##3
\else
##2
\fi
}}
\def\testC{%
\long\def\IfComp##1##2##3{%
%%% Why seven?!?
\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter
\expandafter\ifx\csname macro-##1\endcsname\relax
##3
\else
##2
\fi
}}
\begin{document}
{\testA
\textbf{TEST A}\
\IfComp{1}{defined}{undefined}\
\IfComp{2}{defined}{undefined}\
\textbf{TEST B}\
\protected@edef\x{\IfComp{1}{defined}{undefined}}\expandafter\verb\expandafter|\meaning\x| \
\protected@edef\x{\IfComp{2}{defined}{undefined}}\expandafter\verb\expandafter|\meaning\x| \
}
{\testB
\textbf{TEST C}\
\IfComp{1}{defined}{undefined}\
\IfComp{2}{defined}{undefined}\
\textbf{TEST D}\
\protected@edef\x{\IfComp{1}{defined}{undefined}}\expandafter\verb\expandafter|\meaning\x| \
\protected@edef\x{\IfComp{2}{defined}{undefined}}\expandafter\verb\expandafter|\meaning\x| \
}
{\testC
\textbf{TEST E}\
\IfComp{1}{defined}{undefined}\
\IfComp{2}{defined}{undefined}\
\textbf{TEST F}\
\protected@edef\x{\IfComp{1}{defined}{undefined}}\expandafter\verb\expandafter|\meaning\x| \
\protected@edef\x{\IfComp{2}{defined}{undefined}}\expandafter\verb\expandafter|\meaning\x| \
}
\end{document}
which yields the following output:
\TestA was my first attempt, and it works fine for most cases (cf. TEST A). However, I then came across a situation where I needed to put the whole query inside an \protected@edef{…} and the code crashed, since it leaves the \let\@comp@name\test-1 unexpanded in the stack (cf. TEST B).
So, I played a bit around, and came up with \TestB, hoping that \csname macro-##1\endcsname would expand twice before it is checked against \relax. But that doesn't work, I get "defined" for both cases (see TEST C and D).
After successively adding moar \expandafters, I eventually got it to work with 7 \expandafters (cf. TEST E and F).
My question is: Why do seven \expandafters work in both cases?

\foo:nto\foo:eand automatically expand the argument with as many steps as needed:-: – David Carlisle Mar 06 '24 at 17:44\csnameafter the\ifxtwice, but I now see that I need to expand it actually tree times. Now, the 7\expandafterdo make sense to me. – Lupino Mar 06 '24 at 19:56