Problem with command expansion

I wouldn't use long nested if-tests. This is difficult to expand.

\documentclass{article}

\ExplSyntaxOn
\tl_const:Nn \c__nvaughan_convert_es_tl{spanish}
\tl_const:Nn \c__nvaughan_convert_lat_tl{latin}
\tl_const:Nn \c__nvaughan_convert_eng_tl{english}
\tl_const:Nn \c__nvaughan_convert_deu_tl{german}
\tl_const:Nn \c__nvaughan_convert_enm_tl{spanish}

\newcommand*{\convertlang}[1]
 {
  \tl_if_exist:cTF {c__nvaughan_convert_#1_tl}
   { \tl_use:c {c__nvaughan_convert_#1_tl} }
   { english}
 } 

\newcommand*{\langtest}[1]{%
   \str_if_eq:eeTF {\convertlang{#1}}{spanish}
   {True}{False}}
\ExplSyntaxOff
\begin{document}

Testing: \convertlang{es} % yields: spanish
\convertlang{lat} \convertlang{blub}

\langtest{es} % should yield: True

\end{document}

enter image description here


I'd use the simpler interface of expl3 with \str_case:nnF

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\convertlang}{m}
 {
  \nvaughan_convertlang:n { #1 }
 }

\cs_new:Nn \nvaughan_convertlang:n
 {
  \str_case:nnF { #1 }
   {
    {es}{spanish}
    {lat}{latin}
    {eng}{english}
    {deu}{german}
    {enm}{spanish}
   }
   {english}
 }

\NewExpandableDocumentCommand{\langtest}{m}
 {
  \str_if_eq:eeTF { \nvaughan_convertlang:n { #1 } } { spanish } { True } { False }
 }

\ExplSyntaxOff

\begin{document}

Testing: \convertlang{es} % yields: spanish

\langtest{es} should yield: True

\langtest{lat} should yield: False 

\end{document}

enter image description here


You can use the expandable and expanding string equality test \pdfstrcmp (for portability use \pdf@strcmp from pdftexcmds, see for example Are there any "if" commands like "\ifnum" in LaTeX?). That command expands its test arguments and is itself expandable. (The latter is always nice, the former is not always convenient [cf. https://tex.stackexchange.com/q/230878/35864], but is what we want here.)

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{etoolbox}

\usepackage{pdftexcmds}

\makeatletter
\newcommand*{\IfStrEqualTF}[2]{%
  \ifnum\pdf@strcmp{#1}{#2}=\z@
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}
\makeatother

\newcommand*{\convertlang}[1]{%
  \IfStrEqualTF{#1}{es}
    {spanish}
    {\IfStrEqualTF{#1}{lat}
       {latin}
       {\IfStrEqualTF{#1}{eng}
          {english}
          {\IfStrEqualTF{#1}{deu}
            {german}
            {\IfStrEqualTF{#1}{enm}
               {spanish}
               {english}}}}}}

\newcommand*{\langtest}[1]{%
  \IfStrEqualTF{\convertlang{#1}}{spanish}
    {True}
    {False}}

\begin{document}
Testing: \convertlang{es} % yields: spanish

\langtest{es} % should yield: True

\langtest{en} % should yield: False
\end{document}

Testing: spanish//True//False


Some more details about the MWE and why it doesn't do the desired thing.

First of all, etoolbox's \ifstrequal is defined with \newrobustcmd. It is therefore robust and not expandable. That means that

\edef\temp{\convertlang{#1}}

does not actually save the long language name in \temp as we would have hoped. It just saves a cascade of \ifstrequal tests. This means that \temp does not contain a simple string.

\edef\temp{\convertlang{es}}%
\show\temp

gives

> \temp=macro:
->\ifstrequal {es}{es}{spanish}{\ifstrequal {es}{lat}{latin}{\ifstrequal {es}{eng}{english}{\ifstrequal {es}{deu}{german}{\ifstrequal {es}{enm}{spanish}{english}}}}}.

You need a string equality test that is expandable to allow \convertlang{#1} to expand to the language name in an \edef.

\pdf@strcmp comes in handy here, since its string comparison is expandable, meaning that a command defined via this equality test can expand to the result of the comparisons in an \edef.

The second issue is that \ifstrequal doesn't expand its arguments, so even if \temp contained only a string, the test wouldn't quite work as intended

\def\temp{spanish}%
\ifstrequal{\temp}{spanish}{True}{False}%

still gives 'False', since \temp is not a string equal to spanish, it expands to a string equal to spanish, that is a small but significant difference.

There are several possible ways around that. Depending on what you can guarantee about \temp it would be enough to say

\expandafter\ifstrequal\expandafter{\temp}{spanish}{True}{False}%

or

\ifdefstring{\temp}{spanish}{True}{False}%

if you know that \temp expands to a string in one expansion step. If more steps are required or you want full expansion, other tricks are needed.

\pdf@strcmp helps here, because it just completely expands its argument. This means that as long as \temp does not contain anything that blows up in an expansion context, its 'ultimate expanded string value' can be compared. You need not worry about expanding it first for the test.

Tags:

Expansion