10

I want to define a macro in a group in LaTeX3 so that it stays defined after the group, but without using global variables. For now I do:

\documentclass{article}
\ExplSyntaxOn
\NewDocumentEnvironment{test}{}{}{
  \str_clear_new:N \mytest
  \str_put_right:Nx \mytest {I ~ like ~}
  \str_gset_eq:NN \dummyvar \mytest
  \group_insert_after:N \str_set_eq:NN
  \group_insert_after:N \mytest
  \group_insert_after:N \dummyvar
}
\ExplSyntaxOff
\begin{document}
\def\mytest{chocolate.}
{
  \begin{test}
    I’d like mytest to be defined at the end of the group without using global variables.
  \end{test}
  \mytest
}
\mytest
\end{document}

It works, but it seems really dirty, like I need to introduce a global dummy var etc… I tried to do:

  \group_insert_after:N \str_set_eq:NN
  \group_insert_after:N \mytest
  \expandafter \group_insert_after:N \mytest

But funnily, the string is written backward now!! I also tried to generate a variant of \group_insert_after:V but it is forbidden.

5
  • I might get away with something like \exp_args:NNNNNV \group_insert_after:N \str_set_eq:NN \group_insert_after:N \mytest \group_insert_after:N \mytest (you'll probably have to generate \exp_args:NNNNNV though).
    – gusbrs
    Jul 9, 2023 at 23:49
  • @gusbrs, thanks, so I tried, using \exp_args_generate:n {NNNNV}, but can’t make it work, not sure if I’m doing something stupid…Could you provide a full example by any chance?
    – tobiasBora
    Jul 9, 2023 at 23:59
  • Mhm, sorry, it was just an idea, which I can't seem to make it work either... :-(
    – gusbrs
    Jul 10, 2023 at 0:13
  • (Off-topic, you probably don't want to call \str_clear_new:N \mytest inside the environment, since it may be used multiple times).
    – gusbrs
    Jul 10, 2023 at 0:14
  • You might find this useful: tex.stackexchange.com/q/56294/86 It's an old question so hopefully there are more modern answers so I'll be interested in what advice you get. Jul 10, 2023 at 5:57

3 Answers 3

9

I understand that \dummyvar cannot work if you want to keep the meaning of more than single macro after group. But you can set a control sequence derived from the name of given control sequence:

\def\keepaftergroup#1{%
   \global \expandafter\let \csname x:\string#1\endcsname =#1
   \aftergroup\let
   \aftergroup#1%
   \expandafter\aftergroup \csname x:\string#1\endcsname
   
}

\def\mytest{nazdar}

{
   \def\mytest{Hello}
   \def\mysecond{Hi}
   \keepaftergroup\mytest
   \keepaftergroup\mysecond
   Text in group.%
}
After group: \mytest, \mysecond
5

We do not currently have an 'escape from arbitrary group levels' function, largely as this is an unusual requirement. On the other hand, where you want to set a variable outside a group you control, this is quite straight-forward

\group_begin:
   % Stuff happens
   \str_set:Nn \l__mypkg_str { ... }
\exp_args:NNNV \group_end:
\str_set:Nn \l__mypkg_str \l__mypkg_str

and so on.

3
  • Thanks but this would not work for environments right? I'm surprised, people never try to save a variable from an environment for later use?
    – tobiasBora
    Jul 10, 2023 at 11:56
  • @tobiasBora Environments are group, so usually either things are supposed to be retained insider them or they are globals
    – Joseph Wright
    Jul 10, 2023 at 12:02
  • Well they also have the advantage of not tempering the content, which is practical to deal with verbatim content ^^ (+ they are sometimes more visual for big content)
    – tobiasBora
    Jul 10, 2023 at 17:12
4

The following defines a mechanism to smuggle things up one level with a single auxiliary global token.

It won't work for toks registers. I hope I didn't screw up on other points of the implementation.

\documentclass{article}
\ExplSyntaxOn
\tl_new:N \g__tobiasbora_smuggler_tl
\cs_new:Npn \tobiasbora_smuggle:n #1
  {
    \tl_gset:Nx \g__tobiasbora_smuggler_tl
      {
        \tl_map_function:nN {#1} \__tobiasbora_smuggle:N
        \exp_not:n { \tl_gset:Nn \g__tobiasbora_smuggler_tl }
          { \exp_not:o \g__tobiasbora_smuggler_tl }
      }
    \group_insert_after:N \g__tobiasbora_smuggler_tl
  }
\cs_new:Npn \__tobiasbora_smuggle:N #1
  {
    \token_if_macro:NTF #1
      {
        \exp_not:n { \cs_set:Npx #1 }
          { \exp_not:N \exp_not:n { \exp_not:o #1 } }
      }
      {
        \token_if_chardef:NTF #1
          { \exp_not:n { \chardef #1 } = \exp_not:V #1 \scan_stop: }
          {
            \token_if_mathchardef:NTF #1
              { \exp_not:n { \mathchardef #1 } = \exp_not:V #1 \scan_stop: }
              {
                % assume it's a register type and the following is fine
                #1 = \exp_not:V #1 \scan_stop:
              }
          }
      }
  }
\NewDocumentEnvironment{test}{}{}{
  \str_set:Nn \mytest { like~ }
  \tobiasbora_smuggle:n { \mytest \mycount }
}
\ExplSyntaxOff

\newcount\mycount
\mycount=0

\begin{document}
\def\mytest{chocolate.}
{
  \begin{test}
    I’d like mytest to be defined at the end of the group without using global variables.
    \mycount=1
  \end{test}%
  \uppercase\expandafter{\romannumeral\mycount} % this prints "I"
  \mytest
}%
\mytest
\romannumeral\mycount % this prints nothing as it's 0 again
\end{document}
1
  • How funny one, should be buitin in LaTeX3!
    – tobiasBora
    Jul 10, 2023 at 11:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .