In case you need an expandable empty-test which does without e-TeX-extensions and without forbidden tokens, I can offer this one:
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \CheckWhetherEmpty{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% Due to \romannumeral0-expansion the result is delivered after two
%% expansion-steps/after two "hits" by \expandafter.
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\CheckWhetherEmpty#1{%
\romannumeral0\expandafter\secondoftwo\string{\expandafter
\secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\secondoftwo\string}\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}\firstoftwo\expandafter{} \secondoftwo}%
{\firstoftwo\expandafter{} \firstoftwo}%
}%
Like anything else that works in terms of macros, this does not work with arguments that contain \outer
-tokens.
Deviating from the requirements formulated in the question, \CheckWhetherEmpty
is rather slow.
I take \CheckWhetherEmpty
for a moot thing/for a slow workaround in situations where one can't take for granted that e-TeX's \detokenize
is available/is allowed by the terms of the macro-writing-challenge.
I emphasize that the gist/the basic idea of "hitting" either the first token of the non-empty argument or the closing brace behind the empty argument with \string
and cranking out the brace-cases by removing a brace-balanced argument does not come from me but does come from Robert R. Schneck's \ifempty
-macro.
I just added \romannumeral0
-expansion and stringification and removal of superfluous curly braces via \secondoftwo
in favor of removing superfluous curly braces via \iffalse..\fi
.
I did so for ensuring that things won't break half-way through the expansion-chain due to unbalanced \if..\else..\fi
at some stage popping up that might be contained in the argument or might come into being due to "hitting" the first token of the argument with \string
...
In order to explain how the test works, let's rewrite this with different line-breaking:
\long\def\CheckWhetherEmpty#1{%
\romannumeral0%
\expandafter\secondoftwo\string{%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string#1} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
}%
Let's look at the three scenarios:
Scenario 1: #1
is not empty and #1
's first token is an opening brace—e.g., #1
={foo}bar
:
\CheckWhetherEmpty{{foo}bar}{empty}{not empty}%
Step 1:
\romannumeral0%
\expandafter\secondoftwo\string{%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string{foo}bar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 2: \romannumeral0
-expansion initiated:
%\romannumeral0-expansion in progress:
\expandafter\secondoftwo\string{%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string{foo}bar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 3: \expandafter
"hits" \string
and {
gets stringified:
%\romannumeral0-expansion in progress:
\secondoftwo{12%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string{foo}bar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 4: \secondoftwo
removes {12
:
%\romannumeral0-expansion in progress:
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string{foo}bar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 5: \expandafter
-chain "hits" \string
which in case of the argument not being empty strigifies the argument's first token and in case of the argument being empty stringifies the closing brace:
%\romannumeral0-expansion in progress:
\secondoftwo % <- The interesting \secondoftwo
{% <- Opening brace of interesting \secondoftwo's first argument.
{%
{12foo}bar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 6: The interesting \secondoftwo
acts:
%\romannumeral0-expansion in progress:
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 7: \expandafter
"hits" \string
and }
gets stringified:
%\romannumeral0-expansion in progress:
\secondoftwo}12% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 8: \secondoftwo
removes }12
:
%\romannumeral0-expansion in progress:
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 9: \expandafter
-chain "hits" \string
and }
gets stringified:
%\romannumeral0-expansion in progress:
\firstoftwo{\secondoftwo}12%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 10: \firstoftwo
acts:
%\romannumeral0-expansion in progress:
\secondoftwo}12%
\firstoftwo\expandafter{} \secondoftwo
{empty}{not empty}%
Step 11: \secondoftwo
removes }12
:
%\romannumeral0-expansion in progress:
\firstoftwo\expandafter{} \secondoftwo
{empty}{not empty}%
Step 12: \firstoftwo
acts:
%\romannumeral0-expansion in progress:
\expandafter⟨space token⟩\secondoftwo
{empty}{not empty}%
Step 13: \expandafter
"hits" \secondoftwo
:
%\romannumeral0-expansion in progress:
⟨space token⟩not empty%
Step 14: \romannumeral0
-expansion finds the ⟨space token⟩
and discards it and stops searching for more digits. Thus \romannumeral
finds the non-positive number 0
and therefore terminates without delivering any token in return:
%\romannumeral0-expansion terminated:
not empty%
Scenario 2: #1
is not empty and #1
's first token is not an opening brace—e.g., #1
=foobar
:
\CheckWhetherEmpty{foobar}{empty}{not empty}%
Step 1:
\romannumeral0%
\expandafter\secondoftwo\string{%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string foobar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 2: \romannumeral0
-expansion initiated:
%\romannumeral0-expansion in progress:
\expandafter\secondoftwo\string{%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string foobar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 3: \expandafter
"hits" \string
and {
gets stringified:
%\romannumeral0-expansion in progress:
\secondoftwo{12%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string foobar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 4: \secondoftwo
removes {12
:
%\romannumeral0-expansion in progress:
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string foobar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 5: \expandafter
-chain "hits" \string
which in case of the argument not being empty strigifies the argument's first token and in case of the argument being empty stringifies the closing brace:
%\romannumeral0-expansion in progress:
\secondoftwo % <- The interesting \secondoftwo
{% <- Opening brace of interesting \secondoftwo's first argument.
{%
f12oobar} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 6: The interesting \secondoftwo
acts:
%\romannumeral0-expansion in progress:
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 7: \expandafter
-chain "hits" \string
and }
gets stringified::
%\romannumeral0-expansion in progress:
\firstoftwo{\secondoftwo}12%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 8: \firstoftwo
acts:
%\romannumeral0-expansion in progress:
\secondoftwo}12%
\firstoftwo\expandafter{} \secondoftwo
{empty}{not empty}%
Step 9: \secondoftwo
removes }12
:
%\romannumeral0-expansion in progress:
\firstoftwo\expandafter{} \secondoftwo
{empty}{not empty}%
Step 10: \firstoftwo
acts:
%\romannumeral0-expansion in progress:
\expandafter⟨space token⟩\secondoftwo
{empty}{not empty}%
Step 11: \expandafter
"hits" \secondoftwo
:
%\romannumeral0-expansion in progress:
⟨space token⟩not empty%
Step 12: \romannumeral0
-expansion finds the ⟨space token⟩
and discards it and stops searching for more digits. Thus \romannumeral
finds the non-positive number 0
and therefore terminates without delivering any token in return:
%\romannumeral0-expansion terminated:
not empty%
Scenario 3: #1
is empty:
\CheckWhetherEmpty{}{empty}{not empty}%
Step 1:
\romannumeral0%
\expandafter\secondoftwo\string{%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 2: \romannumeral0
-expansion initiated:
%\romannumeral0-expansion in progress:
\expandafter\secondoftwo\string{%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 3: \expandafter
"hits" \string
and {
gets stringified:
%\romannumeral0-expansion in progress:
\secondoftwo{12%
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 4: \secondoftwo
removes {12
:
%\romannumeral0-expansion in progress:
\expandafter\secondoftwo % <- The interesting \secondoftwo
\expandafter{% <- Opening brace of interesting \secondoftwo's first argument.
\expandafter{%
\string} % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 5: \expandafter
-chain "hits" \string
which in case of the argument not being empty strigifies the argument's first token and in case of the argument being empty stringifies the closing brace:
%\romannumeral0-expansion in progress:
\secondoftwo % <- The interesting \secondoftwo
{% <- Opening brace of interesting \secondoftwo's first argument.
{%
}12 % <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is an opening brace (Scenario 1).
\expandafter
\secondoftwo\string}% <- Closing brace of interesting \secondoftwo's first argument in case #1's first token is not an opening brace (Scenario 2).
\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}%
\firstoftwo\expandafter{} \secondoftwo}% <- Closing brace of interesting \secondoftwo's first argument in case #1 is empty (Scenario 3).
{\firstoftwo\expandafter{} \firstoftwo}%
{empty}{not empty}%
Step 6: The interesting \secondoftwo
acts:
%\romannumeral0-expansion in progress:
\firstoftwo\expandafter{} \firstoftwo
{empty}{not empty}%
Step 7: \firstoftwo
acts:
%\romannumeral0-expansion in progress:
\expandafter⟨space token⟩\firstoftwo
{empty}{not empty}%
Step 8: \expandafter
"hits" \firstoftwo
:
%\romannumeral0-expansion in progress:
⟨space token⟩empty%
Step 9: \romannumeral0
-expansion finds the ⟨space token⟩
and discards it and stops searching for more digits. Thus \romannumeral
finds the non-positive number 0
and therefore terminates without delivering any token in return:
%\romannumeral0-expansion terminated:
empty%
Based on that you can implement an \ifblank
-test as follows:
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%% "fetching" _un_delimited arguments: --
%% \CheckWhetherBlank{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that
%% argument which is to be checked is blank>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not blank}%
\long\def\CheckWhetherBlank#1{%
\romannumeral\expandafter\expandafter\expandafter\secondoftwo
\expandafter\CheckWhetherEmpty\expandafter{\firstoftwo#1{}.}%
}%
Based on the gist of the implementation of \CheckWhetherEmpty
you can implement checking whether a non-delimited argument's first token is an explicit character token of category code 1 (begin group): Just ensure by appending a dot that the \string
which gets carried out right before executing the "interesting \secondoftwo
" never "hits" a closing brace (which implies elimination of scenario 3) and implement forking between scenario 1 and scenario 2:
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%-----------------------------------------------------------------------------
%% \CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
%%
%% Due to \romannumeral0-expansion the result is delivered after two
%% expansion-steps/after two "hits" by \expandafter.
%%
\long\def\CheckWhetherBrace#1{%
\romannumeral0\expandafter\secondoftwo\expandafter{\expandafter{%
\string#1.}\expandafter\firstoftwo\expandafter{\expandafter
\secondoftwo\string}\firstoftwo\expandafter{} \firstoftwo}%
{\firstoftwo\expandafter{} \secondoftwo}%
}%
\detokenize
l3 is older than etex....\if aa\fi
versus\ifx aa\fi
and the latter is slightly faster, but\expandafter\ifx aa\fi
is noticebly slower. However,\if aa\fi
is noticeably slower than\ifx\a\a\fi
(with\def\a{a}
) and\expandafter\ifx\aa\fi
performs just slightly slower than\if aa\fi
.\detokenize
slows down the operation by a considerable amount. However for what I'm trying to do I need the\detokenize
approach because it has to cope with possibly unbalanced conditionals in the argument, in which case other approaches all fail. Thanks for pointing it out!\prg_new_conditional:Npnn
, but instead code the test yourself, because the way\prg_new_conditional:Npnn
sets up the branching is slow (in the produced code, not during the definition, hint: it uses\expandafter
). Instead, if you want the last tiny bit of performance you should use\cs_new:Npn \__pho_fi_use_i:wnn \fi: \use_ii:nn #1 #2 { \fi: #1 } \cs_new:Npn \pho_tl_if_empty:nTF #1 { \if:w \scan_stop: \tl_to_str:n { #1 } \scan_stop: \__pho_fi_use_i:wnn \fi: \use_ii:nn }