3

I am trying to write a LaTeX3 conditional using \prg_new_conditional:Nnn from the expl3 package. Since the code in the conditional needs to make assignments to some variables, I would like to use grouping in order to do those assgnments locally. However, the following MWE fails to compile:

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\prg_new_conditional:Nnn \foo: { p }
{
  \group_begin:
    % Do some stuff.
  \group_end:
  \prg_return_true:
}
\bool_if:nTF { \foo_p: } { true } { false }
\ExplSyntaxOff
\end{document}

The following errors show up several times:

! Missing number, treated as zero.
<to be read again>
\group_begin:
l.11 \bool_if:nTF { \foo_p: }
{ true } { false }
A number should have been here; I inserted `0'.
(If you can't figure out why I needed to see a number,
look up `weird error' in the index to The TeXbook.)
! Missing \endcsname inserted.
<to be read again>
\group_begin:
l.11 \bool_if:nTF { \foo_p: }
{ true } { false }
The control sequence marked <to be read again> should
not appear between \csname and \endcsname.

When I remove \group_begin: and \group_end:, then everything works as expected. So what is going on here? Why can't I use grouping within the definition of \foo_p:?

4
  • 2
    Welcome to TeX.SX! A conditional allowing the predicate form must be fully expandable, but \group_begin: isn't. So, no grouping nor assignments.
    – egreg
    May 11, 2015 at 7:56
  • Such statements appear from time to time in the LaTeX3 interface manual, but I never really understood them. Could you elaborate on this a bit further?
    – ranguwud
    May 11, 2015 at 8:03
  • 2
    consider something simpler like a simple primitive count assignment. you can go \def\z{123}\count0=\z as \z expands to 123, but you can not go \def\z{{123}}\count0=\z as then \z expands to {123} which isn't a legal number. Same here the \group_begin: stops the argument being interpreted as true or false. May 11, 2015 at 8:23
  • 1
    If the code is not expandable, the correct would be \prg_new_protected_conditional:Nnn… which explicitly bans the p branch. So, it's not possible.
    – Manuel
    May 11, 2015 at 12:20

1 Answer 1

6

In order to work, a predicate such as \foo_p: that you'd like to use must expand to either \prg_return_true: or \prg_return_false:, leaving nothing else in the input stream after macro expansion.

In the code for the replacement text you are free to use any of the conditionals marked with a (filled) * in the interface3 manual and, in their true or false parts, only functions marked in the same way.

With your definition, \group_begin: will remain unless hidden in code that's always skipped, but in that case it would be useless. No assignment can be performed in the code for a conditional that's supposed to have a predicate form, because assignments are not performed during macro expansion.

So, for example, it's impossible to have a predicate form for a conditional that's supposed to decide whether some tokens will end up with typeset text, say by doing \hbox_set:Nn \l_tmpa_box {<tokens>} and then measuring the box.

Depending on what you want to do, there could be expandable ways.

6
  • Thanks for your thorough answer, egreg! So could you say that a group by design is never "empty", even though its contents leave nothing in the input stream? What I am actually trying to achieve is to check whether a token list is composed of certain characters. My current approach is to assign an input parameter to a token list and performe some actions on this variable. All this would preferrably be done locally. Finally I can run a test on the token list variable to output \prg_return_true: or \prg_return_false:. Am I missing some obvious way how to turn this into expandable code?
    – ranguwud
    May 11, 2015 at 11:59
  • @ranguwud No code containing assignments can be made fully expandable.
    – egreg
    May 11, 2015 at 12:14
  • @egreg I know an edge case, but a piece of LuaTeX-only code could do the assignment at the Lua end and remain expandable.
    – Joseph Wright
    May 11, 2015 at 14:16
  • @ranguwud Series of expandble loops, outer one over the input data, inner one over the allowed chars?
    – Joseph Wright
    May 11, 2015 at 14:17
  • @JosephWright This sounds indeed like a promising approach, but how would I remember if I found a "forbidden" character without setting a bool or something similar? As far as I know, \tl_break: cannot be used to terminate two loops. At least not without using groups. :)
    – ranguwud
    May 11, 2015 at 15:36

You must log in to answer this question.

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