# \dim_set failed when using \str_if_in in LaTeX3

Here is the MWE:

\documentclass{article}

\begin{document}
\ExplSyntaxOn
\dim_new:N \l__xchoices_coffin_join_horizontal_sep_dim
\str_new:N \l__xchoices_coffin_current_label_pos_str
\str_set:Nn \l__xchoices_coffin_current_label_pos_str {left}
\cs_new:Npn \__xchoices_label_hsep_init:N #1
{
\dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim
{
\str_if_in:NnTF #1 {left}
{ 5pt }
{
\str_if_in:NnTF #1 {right}
{-5pt}
{0pt}
}
}
}
\__xchoices_label_hsep_init:N
\l__xchoices_coffin_current_label_pos_str
% \dim_use:N \l__xchoices_coffin_join_horizontal_sep_dim
\ExplSyntaxOff
\end{document}


but it failed to compile, and the fatal message is:

Missing number, treated as zero
Illegal unit of measure (pt inserted)


I don't know what's going on with it.

• No \…_if_in:… conditional is expandable Feb 3 at 8:26
• Why does it involves in expandability? (I'm a LaTeX3 new learner, not so familiar with it)
– xdyy
Feb 3 at 8:29
• Doesn't the TF of str_if_in come in the input stream or output stream?
– xdyy
Feb 3 at 9:04
• @xdyy Search for "Fully expandable functions" in interface3 then read that, you'll understand. (if you don't, recursively read up on the terms) Feb 3 at 9:10

### A very short course on macro expansion

TeX tokens can be expandable or unexpandable. Macros (those defined with \def or variants thereof, such as \cs_new:Npn) are of the first kind and also some primitives.

When TeX processes its input it first builds tokens and then proceeds to expand tokens, if possible. If not, it sends tokens, absorbed according to the syntax rules of the found unexpandable token, to the next stage (which is outside of user's control).

The \dim_set:Nn instruction is, for instance, unexpandable. In legacy TeX terms you have something similar to

\newdimen\foo
\foo=\dimexpr ... \relax


The token \foo is not expandable, so it's sent to the next stage where it's recognized as a <dimen> register, so TeX requests more input according to the syntax rules, which allow for = and then want something that can be interpreted as a (fixed) length. OK, \dimexpr is good, but it's by itself unexpandable. Same story, it's sent up and down it comes a request for more input.

In this case, macro expansion is performed, so if you had \def\baz{640pt} and TeX finds \baz while providing new tokens for \dimexpr, it will be happy. But if it finds something that doesn't fit the syntax rules for \dimexpr, then it will stop looking for more tokens and the next stage has to use what it had.

### What's going wrong in your case?

The second argument to \dim_set:Nn must only contain tokens that, at the end of the expansion process only leave unexpandable tokens that fit the syntax of \dimexpr.

Unfortunately, \str_if_in:NNTF doesn't. You can deduce it from the fact that in the interface3 manual it is not followed by a star. So the fact that its processing can (and actually does) not at once provide for one of the last two arguments is documented.

### What can you do?

In the picture I added also \str_case:nn(TF) to show that the instructions in this set are expandable.

If your string variable is only expected to contain left or right (so no need to test with \str_if_in:nnTF), you can use \str_case:VnF:

\dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim
{
\str_case:VnF #1
{
{left}{5pt}
{right}{-5pt}
}
{0pt}
}


Otherwise you need to have the \dim_set:Nn instruction inside the cases

    \str_if_in:NnTF #1 {left}
{
\dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim {5pt}
}
{
\str_if_in:NnTF #1 {right}
{
\dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim {-5pt}
}
{
\dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim {0pt}
}
}


In either case, \cs_new:Npn \__xchoices_label_hsep_init:N #1 should be

\cs_new_protected:Npn \__xchoices_label_hsep_init:N #1


because this function performs unexpandable actions.

• Thanks sooo much for your detailed explanation!
– xdyy
Feb 3 at 16:09