3

I would like to see how I can show the advantage of using an expandable command. I made the command \myDimensionCommand but I do not see it is the best way to show the advantages to use fully expandable command.

\documentclass{article}

\ExplSyntaxOff

\NewExpandableDocumentCommand {\convertto} {omm}
  {
    % #1 = number of decimal digits
    % #2 = length
    % #3 = new unit
    \IfNoValueTF {#1}
      {
        \fpeval{\dimeval{#2}/(1#3)}\,#3
      }
         {
           \fpeval{round(\dimeval{#2}/(1#3),#1)}\,#3
         }
  }

% Define a command that uses \convertto
\NewDocumentCommand{\myDimensionCommand}{m m}
  {
    The~converted~dimension~is:~\convertto{#1}{#2}{mm}.
  }

\ExplSyntaxOff

\begin{document}

\convertto[2]{34pt}{cm}

\end{document}
6
  • 2
    Your sample code doesn't employ \myDimensionCommand and doesn't rely on \convertto being (fully) expandable. Please provide an example that actually rests on expandability.
    – Mico
    Mar 3 at 3:40
  • 1
    Expandable commands, using \romannumeral-expansion or \expanded, can be implemented in a way where you know exactly the amount of expansion steps needed for obtaining the result This is very useful when implementing routines which re-arrange tokens to form arguments for other routines. This also is very useful when implementing routines which call other routines or instances of themselves as "sub-processes" and where you need to know exactly when the result of the "sub-process" is there. Mar 3 at 9:04
  • In your example the 1st argument of \convertto is optional an must be in square brackets [...] but it seems that with the call to \convertto coded into the definition of \myDimensionCommand that argument is in curly braces {...} which won't work out as expected. Mar 3 at 14:40
  • You are right. Still doing \rule{\convertto[2]{34pt}{cm}} gives me a missing number.
    – Ragonese
    Mar 3 at 14:49
  • 1
    @Ragonese The syntax of \rule is: \rule[raise-height]{width}{thickness}. As width you give \convertto[2]{34pt}{cm}, but do you also give a thickness? And the definition of \convertto contains \,, which is not a valid token when specifying TeX <dimension> quantities like width/thickness. E.g., {\def\,{}\rule{\convertto[2]{34pt}{cm}}{\convertto[2]{34pt}{cm}}} yields a nice square. Mar 3 at 19:00

5 Answers 5

5

A difference can for example be demonstrated in the bookmarks:

\documentclass{article}

\ExplSyntaxOff

\NewExpandableDocumentCommand {\convertto} {omm}
  {
    % #1 = number of decimal digits
    % #2 = length
    % #3 = new unit
    \IfNoValueTF {#1}
      {
        \fpeval{\dimeval{#2}/(1#3)}\,#3
      }
         {
           \fpeval{round(\dimeval{#2}/(1#3),#1)}\,#3
         }
  }

\NewDocumentCommand {\xconvertto} {omm}
  {
    % #1 = number of decimal digits
    % #2 = length
    % #3 = new unit
    \IfNoValueTF {#1}
      {
        \fpeval{\dimeval{#2}/(1#3)}\,#3
      }
         {
           \fpeval{round(\dimeval{#2}/(1#3),#1)}\,#3
         }
  }


\ExplSyntaxOff
\usepackage{hyperref}
\begin{document}

\section{ Test: \convertto[2]{34pt}{cm}}

\section{ Test: \xconvertto[2]{34pt}{cm}}
\end{document}

This prints the same:

enter image description here

But the bookmarks look quite different:

enter image description here

3
  • Could you provide a different example, ane using \convertto, the other using \xconvertto, as I cannot see the bookmarks with evince.
    – Ragonese
    Mar 3 at 12:42
  • Have now figured out that doing \rule{\xconvertto[2]{34pt}{cm}}{0.3pt} with the non-expandable version fails.
    – Ragonese
    Mar 3 at 15:13
  • 1
    Evince should show bookmarks (be they call them outline) Mar 3 at 22:11
5

The difference is that one is expandable and the other isn't, so inside an \edef for instance, one is fully expanded, the other isn't, you can use an expandable command inside other expressions whereas you can't use an unexpandable command there. So one is easier to nest in other code, the other requires you to set a macro with the result as part of your code, so that further code can use that macro to do additional steps on the result. Simple document illustrating the difference in behaviour in an \edef:

\documentclass{article}

\ExplSyntaxOn

\NewExpandableDocumentCommand \convertto { o m m }
  {
    % #1 = number of decimal digits
    % #2 = length
    % #3 = new unit
    \IfNoValueTF {#1}
      { \fpeval{\dimeval{#2}/(1#3)}\,#3 }
      { \fpeval{round(\dimeval{#2}/(1#3),#1)}\,#3 }
  }
\NewDocumentCommand \protectedconvertto { o m m }
  {
    % #1 = number of decimal digits
    % #2 = length
    % #3 = new unit
    \IfNoValueTF {#1}
      { \fpeval{\dimeval{#2}/(1#3)}\,#3 }
      { \fpeval{round(\dimeval{#2}/(1#3),#1)}\,#3 }
  }

\ExplSyntaxOff

\begin{document}
\ttfamily

\edef\foo{\convertto{2cm}{mm}}\meaning\foo

\edef\foo{\protectedconvertto{2cm}{mm}}\meaning\foo
\end{document}

enter image description here

5

It's not really a matter of advantage and disadvantage, as choosing a suitable definition, only you can do that. Also note you are adding lots of space from ends of lines in your definition as you are missing \ExplSyntaxOn

Here I use your definition in a protected command, as you had it and as an expandable command, and then use them both in an expansion context (\typeout) You get

\myDimensionCommandA {34pt}{cm}
The converted dimension is: 1.194963331949633\,cm{mm}.

so which command is advantageous depends on which output you need in the real context.

\documentclass{article}

\ExplSyntaxOn% ON!!!!!

\NewExpandableDocumentCommand {\convertto} {omm}
  {
    % #1 = number of decimal digits
    % #2 = length
    % #3 = new unit
    \IfNoValueTF {#1}
      {
        \fpeval{\dimeval{#2}/(1#3)}\,#3
      }
         {
           \fpeval{round(\dimeval{#2}/(1#3),#1)}\,#3
         }
  }

% Define a protected command that uses \convertto
\NewDocumentCommand{\myDimensionCommandA}{m m}
  {
    The~converted~dimension~is:~\convertto{#1}{#2}{mm}.
  }

% Define an expandable command that uses \convertto
\NewExpandableDocumentCommand{\myDimensionCommandB}{m m}
  {
    The~converted~dimension~is:~\convertto{#1}{#2}{mm}.
  }

\ExplSyntaxOff

\begin{document}



\typeout{\myDimensionCommandA{34pt}{cm}}

\typeout{\myDimensionCommandB{34pt}{cm}}

\end{document}

Note the second one here looks perhaps more readable but it only shows one part of its internal definition \, other commands may show many more internal implementation details that you would never want to show in such a context, so the choice of which style to use depends on the command and the context in which it is likely to be used.

11
  • Why does the following not print the result of typeout \NewDocumentCommand {\myDimensionCommandC} {} { The~converted~dimension~is: ~\typeout {\convertto [2] {34pt} {cm}} }
    – Ragonese
    Mar 3 at 11:11
  • @Ragonese what do you mean by "print" ? that command puts the text The converted dimension is: into the pdf output, and puts the converted value onto the terminal and log. both writing to the document and writing to the terminal are colloquially called "print" in different contexts. Mar 3 at 11:17
  • I do not see The converted dimension is: 1.19cm in the pdf when calling \myDimensionCommandC.
    – Ragonese
    Mar 3 at 11:24
  • @Ragonese if you add \NewDocumentCommand {\myDimensionCommandC} {} { The~converted~dimension~is: ~\typeout {\convertto [2] {34pt} {cm}} } to the ExplSyntax region and add \myDimensionCommandC before end document you get a pdf from the above that looks like this Mar 3 at 11:58
  • 1
    @Ragonese obviously the 1.19cm does not appear in the pdf, that appears on the terminal. Mar 3 at 11:59
3

In the example below, \testA is defined with \NewDocumentCommand and \testB is defined with \NewExpandableDocumentCommand. If used as an option to \draw then \testA{red} fails because \testA{red} is not recognized as a color. The option \testB{red} does work because \testB is defined with \NewExpandableDocumentCommand.

enter image description here

\documentclass[border=6pt]{standalone}
\usepackage{tikz}
\NewDocumentCommand\testA{m}{#1}
\NewExpandableDocumentCommand\testB{m}{#1}
\begin{document}
\begin{tikzpicture}
%\draw[\testA{red}] (0,0)--(1,1);%fails
\draw[\testB{red}] (0,0)--(1,1);%works
\end{tikzpicture}
\end{document}
0
2

Another scenario where expandability is useful is creating external text-files, e.g., comma-separated-value-lists/.csv-files holding contents of databases, that are to be processed by programs other than TeX which don't understand LaTeX sytax so that all control sequence tokens in the stuff to be written to external text file should go away via expansion.

The following example produces two .csv-files via the package datatool.

(The command \verbatiminput from the package verbatim is used so that you can see the content of these files within the pdf-file which results from compiling.)

The second of these files comes from entries where the non-expandable command is used, thus it contains things like \myDimensionCommand {2}{2.54cm} which you probably don't want in a .csv-file that is to be processed further by software other than LaTeX.

\documentclass{article}
\usepackage{datatool, verbatim}

% \ExplSyntaxOn
% \cs_new_eq:NN \fpeval \fp_eval:n
% \cs_new_eq:NN \dimeval \dim_eval:n
% \ExplSyntaxOff

\NewExpandableDocumentCommand {\convertto} {omm}
  {%%
    % #1 = number of decimal digits
    % #2 = length
    % #3 = new unit
    \IfNoValueTF {#1}
      {%%
        \fpeval{\dimeval{#2}/(1#3)}\,#3%%
      }
      {%%
        \fpeval{round(\dimeval{#2}/(1#3),#1)}\,#3%%
      }%%
  }

% Define a command that uses \convertto
\NewDocumentCommand{\myDimensionCommand}{m m}
  {%% The first argument of \convertto is optional and therefore
   %% must be in square brackets!!!
    The converted dimension is: \convertto[{#1}]{#2}{mm}.%%
  }

% Create .csv-files holding values of length quantities 
% expressed as multiples of different units:

\begingroup

\def\,{}% \convertto beneath other things delivers a
        % control symbol token \,  , which in turn in LaTeX
        % is defined as a protected/an unexpandable command
        % for producing a thin horizontal space (.16667em).
        % We want neither a thinspace nor the control sequence
        % \, to be written to text files, thus within a local
        % scope/a group redefine \, so that it just vanishes
        % when being expanded.

\DTLnewdb{Database}%
\DTLaddcolumn{Database}{ValueToConvert}%
\DTLsetheader{Database}{ValueToConvert}{dimension to convert}%
\DTLaddcolumn{Database}{ConversionResults}%
\DTLsetheader{Database}{ConversionResults}{result of conversion}%
\dtlexpandnewvalue
\DTLnewrow{Database}%
\DTLnewdbentry{Database}{ValueToConvert}{2.54cm}%
\DTLnewdbentry{Database}{ConversionResults}{\convertto[2]{2.54cm}{in}}%
\DTLnewrow{Database}%
\DTLnewdbentry{Database}{ValueToConvert}{65536sp}%
\DTLnewdbentry{Database}{ConversionResults}{\convertto[2]{65536sp}{pt}}%
\dtlnoexpandnewvalue
\DTLsavedb{Database}{Entriesxpanded.csv}%

\DTLcleardb{Database}%

\DTLaddcolumn{Database}{ValueToConvert}%
\DTLsetheader{Database}{ValueToConvert}{dimension to convert}%
\DTLaddcolumn{Database}{ConversionResults}%
\DTLsetheader{Database}{ConversionResults}{result of conversion}%
\dtlexpandnewvalue
\DTLnewrow{Database}%
\DTLnewdbentry{Database}{ValueToConvert}{2.54cm}%
\DTLnewdbentry{Database}{ConversionResults}{\myDimensionCommand{2}{2.54cm}}%
\DTLnewrow{Database}%
\DTLnewdbentry{Database}{ValueToConvert}{72bp}%
\DTLnewdbentry{Database}{ConversionResults}{\myDimensionCommand{2}{72bp}}%
\dtlnoexpandnewvalue
\DTLsavedb{Database}{Entriesunexpanded.csv}%

\DTLcleardb{Database}%

\endgroup

\begin{document}

% The content of the  .csv-files

Here is the content of the file "Entriesxpanded.csv" which holds entries coming from expandable commands:

\verbatiminput{Entriesxpanded.csv}

Here is the content of the file "Entriesunexpanded.csv" which holds entries coming from unexpandable commands:

\verbatiminput{Entriesunexpanded.csv}

\end{document}

enter image description here

You must log in to answer this question.

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