16

In the latest CTAN update, \bool_if:n(TF) has been made greedy: ... Any code which still assumes lazy evaluation for \bool_if:n(TF) will therefore need adjustment.

What does it mean that \bool_if:n(TF) is greedy? What does lazy evaluation mean in this context?

Am I right to assume that \bool_if:NT \l__variable_bool will not be affected?

1 Answer 1

17

A “greedy” evaluation of boolean expressions means that every subexpression is evaluated and only after this the full boolean expression returns its value.

An evaluation is “lazy” when only the parts necessary to decide about truth or falsehood of the expression are evaluated.

In the expl3 language, one can build up complex boolean expressions using logical operators “and” &&, “or” || or “not” ! together with predicates and parentheses.

Since the last update of expl3, such boolean expressions, used as the first argument to \bool_if:nTF, are subject to greedy evaluation for implementation reasons. In particular, every subexpression needs to return a boolean value.

However, expl3 also provides lazy evaluation with special functions.

An example where the difference is important is the issue with unicode-math that came to the light with the update, see unicode-math failing to compile after update of TL 2017

The current code (to be fixed) in unicode-math has

\bool_if:nTF { \tl_if_single_p:n {##1} && \token_if_cs_p:N ##1 }
  { \seq_put_right:Nn \l_@@_cmd_range_seq {##1} }
  { \seq_put_right:Nn \l_@@_char_range_seq {##1} }

The first subexpression returns true if the argument is a single token; with lazy evaluation, if this returns false, it's not necessary to examine the second part in order to conclude the condition is false. With the greedy evaluation, the second part is examined also when the first part returns false.

The object of the function is to distinguish whether the value given to the range keyword in the options to \setmainfont consists of a single token which is a control sequence. What happened was that range=`\+ didn't pass the test \tl_if_single_p:n, so the second conditional was not attempted; but with greedy evaluation, the second test is performed nonetheless, resulting in an error, because `\+ is not valid input for \token_if_cs_p:N.

The fix is to use lazy evaluation:

\bool_lazy_and:nnTF { \tl_if_single_p:n {##1} } { \token_if_cs_p:N ##1 }
  { \seq_put_right:Nn \l_@@_cmd_range_seq {##1} }
  { \seq_put_right:Nn \l_@@_char_range_seq {##1} }

which will do the same as before.

The “lazy” functions have lazy in their name:

\bool_lazy_all:nTF
\bool_lazy_any:nTF
\bool_lazy_and:nnTF
\bool_lazy_or:nnTF

The first two want, as their first argument, any number of conditional expressions, each surrounded by braces, so

\bool_lazy_all:nTF { {<cond-1>} {<cond-2>} ... { <cond-n> } }
 { <true code> }
 { <false code> }

will follow the true branch if all conditions return true, but will stop evaluation as soon as one condition returns false; similarly

\bool_lazy_any:nTF { {<cond-1>} {<cond-2>} ... { <cond-n> } }
 { <true code> }
 { <false code> }

will follow the true branch if any of the conditions returns true, and will stop as soon as one does.

The other two receive two boolean expressions as their first argument and are streamlined versions for the most common cases.

The change obviously doesn't affect \bool_if:NTF, because this just examines one conditional. However there was another change related to conditionals: something like

\bool_if:NTF \foo { <true> } { <false> }

followed the false branch when \foo wasn't a conditional. This doesn't happen any longer (and it's the cause for the break in mhchem).


At some point in time, the idea had been that \bool_if:nTF would perform lazy evaluation, but this revealed difficult and unmanageable, so the lazy evaluation functions were introduced for just the simple cases of “and” and “or”. This happened well after the code of unicode-math had been written.

7
  • +1 Amazing! Are you part of the development of LaTeX ? Jul 24, 2017 at 21:14
  • 3
    I have read about this on the L3 mailing list and now here and I must say that the terms »lazy« and »greedy« are completely wrong in that context. First of all the opposite of »lazy« is »eager« (or maybe »exhaustive«) and second lazy evaluation means something completely different. What we is described here as »lazy« is in fact a short-circuiting operator and the »greedy« variant a non-short-circuiting. Jul 24, 2017 at 21:20
  • @HenriMenke The terminology is used in Perlistan
    – egreg
    Jul 24, 2017 at 21:21
  • @egreg Video: The Perl Jam: Exploiting a 20 Year-old Vulnerability. With that in mind I wouldn't trust Perl in anything. Jul 24, 2017 at 21:23
  • @HenriMenke Apples and oranges.
    – egreg
    Jul 24, 2017 at 21:28

You must log in to answer this question.

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