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:?

  • 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 '15 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 '15 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. – David Carlisle May 11 '15 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 '15 at 12:20
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.

  • 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 '15 at 11:59
  • @ranguwud No code containing assignments can be made fully expandable. – egreg May 11 '15 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 '15 at 14:16
  • @ranguwud Series of expandble loops, outer one over the input data, inner one over the allowed chars? – Joseph Wright May 11 '15 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 '15 at 15:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.