I'm having to store a non-binary status, what would be the more idiomatic LaTeX3 way of doing it? For instance, suppose that there are 3 possible ways to format a manuscript: long/short/medium. What is the best way to do it in LaTeX3?

I can think of multiple possibilities:

  1. Defining a bunch of int_const constants (fixed at 1, 2, 3, etc.) to name the possible states and then use a global integer int_new to store the current state.

    This seems straightforward, but also very expensive. If my understanding is correct, each integer constant takes a counter register in the current LaTeX3 design.

  2. Using tokens to represent the possible states. In this case, I'd like the tokens used for the possible states to be declared before usage in the code for readability. So, one could have a tl_const (equal to 1, 2, 3, etc. for each possible state) and then use a global token list variable to hold the current state.

    This seems more efficient and possibly even more straightforward, but I do not like my "enum" labels to be named as c_xxx_xxx_tl since I think that naming them as token lists worsens the readability of the code.

Any idea on the matter?

  • Is there a particular reason you want an integer not simply the text short, medium, long? That would naturally be stored as \c_my_format_tl then can be checked using case function or a name lookup (\use:c { ...}).
    – Joseph Wright
    Nov 9, 2019 at 8:02
  • 1
    \int_new:N \c_state_int \if_case:w \c_state_int <case 0> \or: <case 1> \or: ... \else: <default> \fi: is by far the fastest. Nov 9, 2019 at 9:42
  • I had a similar use case and went for defining quark-like macros \c_state_a:, \c_state_b: etc., and then checked against them via \cs_if_eq:NN(TF).
    – siracusa
    Nov 9, 2019 at 15:06
  • @Joseph The reason for not using mere text is the desire to catch errors early. If you declare the enum labels beforehand by defining them as some kind of constant, when you assign the state as in \tl_set_eq:NN \g_xxx_state_switch \c_xxx_state_loong getting the long wrong, you immediately error out. Conversely if you use plain strings as in \tl_gset:Nn \g_xxx_state_switch { loong } you only catch the error at usage time or in the worse case you get strange behavior.
    – callegar
    Nov 9, 2019 at 15:15
  • @siracusa Is there any place where I can see a full sample of your code?
    – callegar
    Nov 9, 2019 at 15:16

1 Answer 1


Here is an example of the approach mentioned in the comments.

For each value <foo> of the enumeration, we define a new function \c_<foo>_manuscript which has \c_<foo>_manuscript_undefined: as its replacement text. (The real quark-like approach would be making it expand to \c_<foo>_manuscript itself, but in my experience expanding to an undefined control sequence works equally fine and is easier to debug in case of an unexpected expansion of the function.)

So the "enumeration" looks like

\cs_new_protected:Npn \c_short_manuscript  { \c_short_manuscript_undefined:  }
\cs_new_protected:Npn \c_medium_manuscript { \c_medium_manuscript_undefined: }
\cs_new_protected:Npn \c_long_manuscript   { \c_long_manuscript_undefined:   }

Storing one of these values works by \let-assigning a control sequence to the corresponding constant:

\cs_set_eq:NN \g__callegar_cur_manuscript \c_medium_manuscript

Finally, to check the current value against a certain constant, we use

\cs_if_eq:NNTF \g__callegar_cur_manuscript \c_medium_manuscript { true } { false }

Full example document:



% The "enumeration" values
\cs_new_protected:Npn \c_short_manuscript  { \c_short_manuscript_undefined:  }
\cs_new_protected:Npn \c_medium_manuscript { \c_medium_manuscript_undefined: }
\cs_new_protected:Npn \c_long_manuscript   { \c_long_manuscript_undefined:   }

% CS holding the current manuscript type value
\cs_set_eq:NN \g__callegar_cur_manuscript \c_medium_manuscript

% Set current manuscript type
\NewDocumentCommand \SetManuscriptType { m } {
    \cs_if_exist:cTF { c_#1_manuscript }
        { \cs_set_eq:Nc \g__callegar_cur_manuscript { c_#1_manuscript } }
        { \PackageError { callegar } { Manuscript~type~`#1'~not~defined } { } }

% Switch over the current manuscript type 
\NewDocumentCommand \SwitchManuscriptType { +m+m+m } {
    \cs_if_eq:NNTF \g__callegar_cur_manuscript \c_short_manuscript { #1 } {
        \cs_if_eq:NNTF \g__callegar_cur_manuscript \c_medium_manuscript { #2 } {
            \cs_if_eq:NNTF \g__callegar_cur_manuscript \c_long_manuscript { #3 }
                { \PackageError { callegar } { Unknown~manuscript~type~set } { } }



Current manuscript type: \SwitchManuscriptType{short}{medium}{long}

Current manuscript type: \SwitchManuscriptType{short}{medium}{long}

Current manuscript type: \SwitchManuscriptType{short}{medium}{long}



enter image description here

  • Thanks. I am now seeing how you are using a protected cs. I also see that you are not using the common termination conventions for the enum constants which would indeed look a bit ugly. I was doing something very similar using token lists instead of command sequences... so I now see it made some sense.
    – callegar
    Nov 12, 2019 at 13:05

You must log in to answer this question.

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