# expl3 replacement for \lowercase trick

Given this example code:

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn

\group_begin:
\char_set_catcode_active:n { \* }
\cs_new_protected:Npn \foo #1
{
\group_begin:
\char_set_catcode_active:N \
\char_set_lccode:nn { \* } { \  }
\tex_lowercase:D { \cs_set:Npn * } { Y }
%    \tl_lower_case:n { \cs_set:Npn * } { Y }
#1
\group_end:
}
\group_end:

\ExplSyntaxOff

\begin{document}
\begingroup
\catcode\ \active
\gdef {X}
\gdef\fooarg{   }
\endgroup

\fooarg

\expandafter\foo\expandafter{\fooarg}
\end{document}


Within the argument of \foo active space characters are re-mapped to a new meaning. To not bring an active space into scope for the whole definition of \foo, the \lowercase trick was used.

There are two issues with this code:

1. The relevant line of the trick uses a "do not use" function, namely \tex_lowercase:D. Is there a more idiomatic way for replacing that whole pattern using expl3-only functions?
2. According to the documentation of \char_set_lccode:nn, \tl_lower_case:n seem to be the "official" \tex_lowercase:D replacement function. But when used in the above code it produces an error

! Undefined control sequence.
<argument> *


not only for * but also for several other characters. Why does this happen?

• The \char_set_catcode_active:N \ line seems quite suspicious – egreg Jun 24 '19 at 8:52
• Note that \tl_lower_case:n is explicitly documented as being about converting 'text' case: it's not intended for any weird catcode stuff. – Joseph Wright Jun 24 '19 at 9:40

There's no need to use the \lowercase trick (at the expense of defining an additional function) for locally setting the meaning of an active character, thanks to \char_set_active_eq:nN.

However, you should not define \foo with an argument, or the argument would be collected and tokenized before the space becomes active.

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn

\cs_new_protected:Npn \foo
{
\group_begin:
\cs_set:Npn \siracusa_test: { Y }
\char_set_active_eq:nN { \  } \siracusa_test:
\char_set_catcode_active:n { \  }
\__siracusa_foo:n
}
\cs_new_protected:Npn \__siracusa_foo:n #1
{
#1
\group_end:
}

\ExplSyntaxOff

\begin{document}
\begingroup
\catcode\ \active
\gdef {X}
\gdef\fooarg{   }
\endgroup

\fooarg

\expandafter\foo\expandafter{\fooarg}
\end{document}


• Perhaps worth adding that the reason we have \char_set_active_eq:nN` using a second function is that the latter should be a well-defined and documented code-level construct. – Joseph Wright Jun 24 '19 at 9:41