Define global function within \begingroup … \endgroup

definitiongroupingl3keys

I have a command that accepts optional arguments as key-value pairs, handled by the l3keys package. I use \begingroup and \endgroup to \keys_set:nn the keys in order to process set options only for the current command invocation.

However, this command shall make some global Document command available to the end user, using \NewDocumentCommand. Due to grouping, this will only be local.
However, a \global before the definition results in the error

! You can't use a prefix with `\begingroup'.
<to be read again> 
                   \group_begin: 
l.93 ... = H, description = ab fg.]{Abfg}{Ab_{fg}}
                                                  
? h
I'll pretend you didn't say \long or \outer or \global or \protected.

? 

so it seems that this is not intended.


The following is a minimal (non-) working example, not using the grouping and thus overwriting the set keys.
The intended grouping and global definition is marked in the comments, adding these lines produces the error above.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\keys_define:nn { categories }
  {
    key       .tl_set:N   = \l__category_key_tl,
    key       .default:n  = \c_novalue_tl
    % More keys ommited
  }

\cs_generate_variant:Nn\tl_if_novalue:nF{ V F }

\NewDocumentCommand{\DeclareCategory}{ O{} m}
  {
  %  \begingroup
      \keys_set:nn{ categories }{#1}
 %     \exp_args:NNc\global\NewDocumentCommand{#2}{ }
      \exp_args:Nc\NewDocumentCommand{#2}{ }
        {
          %Do some stuff here with #2:
          called:~#2,~
          % Do some other stuff, using the \l__category_key_tl token list
          Value~is:~\tl_use:N\l__category_key_tl
        }
%    \endgroup
  }

\ExplSyntaxOff

\begin{document}
  \DeclareCategory[ key = foo]{A}
  \DeclareCategory[ key = foo2]{B}
  
  \A

  \B
\end{document}

How can I make such a global definition within the group?

Best Answer

There are two problems here:

  1. \NewDocumentCommand is not a global declaration;
  2. you want to use the values you set to the keys in the replacement text, not the token list names.

The first problem could be solved by first setting the keys to a “standard state” so avoiding the need for a group.

The second problem is solved by “controlling the expansion” using an expanded definition. Here I use the group strategy and \cs_new_protected:cpx which acts globally.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\keys_define:nn { categories }
  {
    key       .tl_set:N   = \l__category_key_tl,
    key       .default:n  = \c_novalue_tl
    % More keys omitted
  }

\prg_generate_conditional_variant:Nnn \tl_if_novalue:n { V } { p, T, F, TF }

\NewDocumentCommand{\DeclareCategory}{ O{} m}
  {
    \keissler_category_define:nn { #1 } { #2 }
  }

\cs_new_protected:Nn \keissler_category_define:nn
  {
     \group_begin:
     \keys_set:nn { categories }{#1}
     \cs_new_protected:cpx { #2 }
       {
         \exp_not:n
           {
             %Do some stuff here with #2:
             called:~#2,~Value~is:~
           }
         \exp_not:V \l__category_key_tl
       }
     \group_end:
  }

\ExplSyntaxOff

\begin{document}

\DeclareCategory[key = foo]{A}
\DeclareCategory[key = foo2]{B}
  
\A

\B

\end{document}

enter image description here

A different approach, which can be used when you have a small number of keys.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\keys_define:nn { categories }
 {
  key       .tl_set:N   = \l__category_key_tl,
  key       .default:n  = \c_novalue_tl
  % More keys omitted
 }

\prg_generate_conditional_variant:Nnn \tl_if_novalue:n { V } { p, T, F, TF }

\NewDocumentCommand{\DeclareCategory}{ O{} m }
 {
  \group_begin:
  \keys_set:nn { categories } { #1 }
  \keissler_category_define:nnV { #1 } { #2 } \l__category_key_tl
  \group_end:
 }

\cs_new_protected:Nn \keissler_category_define:nnn
 {
  \cs_new_protected:cpn { #2 }
   {
    %Do some stuff here with #2:
    called:~#2,~Value~is:~#3
   }
 }
\cs_generate_variant:Nn \keissler_category_define:nnn { nnV }

\ExplSyntaxOff

\begin{document}

\DeclareCategory[ key = foo]{A}
\DeclareCategory[ key = foo2]{B}
  
\A

\B

\end{document}