7

I am trying to use a global boolean variable b within a LaTeX3 function. I would like my function to print F if b is false and toggle it to true, and to print T if b is true.

This works great in text and regular math, but always prints T if I try to use it within amsmath environments like gather or align. It looks like amsmath is expanding/evaluating the contents of its environments at least once before the final render. How can I avoid this / fix my function?

Here is a MWE, where I stripped out good practices like \NewDocumentCommand and \cs_new_protected:Npn to get it as minimal as possible:

\documentclass{minimal}

\usepackage{amsmath}
\usepackage{etoolbox}

\ExplSyntaxOn
\NewDocumentCommand{\defit}{m}{
  \bool_new:c    { g_it_#1_used  }
}
\NewDocumentCommand{\getit}{m}{
  \bool_if:cTF { g_it_#1_used } { T } { F\bool_gset_true:c { g_it_#1_used } }
}
\ExplSyntaxOff

\begin{document}

\defit{test}\getit{test}\getit{test}\getit{test}

\defit{eqn}
\[
  \getit{eqn} \getit{eqn} \getit{eqn}
\]

\defit{ams}
\begin{gather*}
  \getit{ams} \getit{ams} \getit{ams}
\end{gather*}

\end{document}

The expected output is FTT three times. Instead, I get FTT, FTT, TTT.

2
  • note you will have the same issue with any text that is set twice eg captions, tabularx etc Jun 11, 2021 at 6:01
  • Unrelated: not a good idea to use the minimal class, it is only for internal testing. Might be better to use the standard article class.
    – daleif
    Jun 11, 2021 at 14:46

1 Answer 1

14

Some amsmath environments, like gather or align, do not typeset their contents only once: the do once for measuring (and then discard), and another time for actually typesetting.

Your \getit function, however, sets the boolean globally as soon as it's used for the first time (which for the gather environment happens while measuring), and the output of the measuring run (which is FTT) is discarded, and then you only see TTT for the typesetting run.

You can use the \ifmeasuring@ conditional to detect if you are in the measuring run, and in that case skip changing the boolean:

\documentclass{minimal}

\usepackage{amsmath}
\usepackage{etoolbox}

\ExplSyntaxOn
\NewDocumentCommand \defit { m }
  { \bool_new:c { g_it_#1_used_bool } }
\NewDocumentCommand \getit { m }
  {
    \bool_if:cTF { g_it_#1_used_bool }
      { T }
      {
        F
        \legacy_if:nF { measuring@ }
          { \bool_gset_true:c { g_it_#1_used_bool } }
      }
  }
\ExplSyntaxOff

\begin{document}

\defit{test}\getit{test}\getit{test}\getit{test}

\defit{eqn}
\[
  \getit{eqn} \getit{eqn} \getit{eqn}
\]

\defit{ams}
\begin{gather*}
  \getit{ams} \getit{ams} \getit{ams}
\end{gather*}

\end{document}

Note that the output while measuring should be the same width as the output while typesetting, otherwise your output may look misaligned.

You must log in to answer this question.

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