How can this complex command be broken down and explained for someone starting with Tex

datatoolmacrosprotected-macro

I got this Tex command, as a solution to a different problem and it works. But I have trouble understanding the details of how it works since I'm not that proficient in Latex coding and I want to learn.

%\DTLfetchifnull{<db name>}%
%               {<column1 name>}%
%               {<column1 value>}%
%               {<column2 name>}%
%               {<tokens in case <column2-value> is not a null-value>}%
%               {<tokens in case <column2-value> is a null-value>}%
%               {<tokens in case <column1-value> does not exist>}%
%
% Both with <tokens in case <column2-value> is not a null-value> and with
% <tokens in case <column2-value> is a null-value> the macro
% \dtlcurrentvalue is defined to yield the value of the column whose name
% is <column2 name>.
%
\makeatletter
\@ifundefined{NewDocumentCommand}%
{\newcommand\DTLfetchifnull[7]}%
{\NewDocumentCommand\DTLfetchifnull{mmmmmmm}}%
{%
  \begingroup%
    \protected@edef\@dtl@dogetrowforvalue{%
        \noexpand%
  \endgroup
  \noexpand\dtlgetrowindex{\noexpand\dtl@rowidx}{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
  }\@dtl@dogetrowforvalue%
  \ifx \dtl@rowidx\dtlnovalue%
    \expandafter\@secondoftwo%
  \else%
    \expandafter\@firstoftwo%
  \fi%
  {%
    \edtlgetrowforvalue{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
    \dtlgetentryfromcurrentrow{\dtlcurrentvalue}{\dtlcolumnindex{#1}{#4}}%
    \DTLifnull{\dtlcurrentvalue}{#6}{#5}% 
  }{%
    #7%
  }%
}%
\makeatother
  • The \makeatletter … \makeatother as I understand is necessary, because we change something fundamental about NewDocumentCommand and newcommand?
  • @ifundefined{NewDocumentCommand}% as I understand makes it possible to use NewDocumentCommand instead of newcommand without \usepackage{xparse}?

Why do we need this? Can we just use \usepackage{xparse} and skip the whole:

\makeatletter
\@ifundefined{NewDocumentCommand}%
{\newcommand\DTLfetchifnull[7]}%
...
\makeatother

Or is there an advantage in doing this way?

  • \begingroup … \endgroup as I understand is something likeĀ '{ }' but with different behavior

  • but it enclosed "\protected@edef@dtl@dogetrowforvalue{" so the "}" is outside, how does that work?

    \begingroup%
    \protected@edef@dtl@dogetrowforvalue{%
    \noexpand%
    \endgroup
    \noexpand\dtlgetrowindex{\noexpand\dtl@rowidx}{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
    }@dtl@dogetrowforvalue%

I find it very hard to read how these functions are encapsulated and to decide what is an argument of which function or just following after a function.

This says check if(\dtl@rowidx\dtlnovalue) do \expandafter@secondoftwo else \expandafter@firstoftwo.

  \ifx \dtl@rowidx\dtlnovalue
    \expandafter\@secondoftwo
  \else
    \expandafter\@firstoftwo
  \fi

Are @firstoftwo and @secondoftwo the two options in curly braces that are coming after this?

Best Answer

\makeatletter and \makeatother are needed in order to allow using command names with @ in them. There is nothing really special about it.

The first idea is to use \NewDocumentCommand{\DTLfetchifnull}{mmmmmmm} if it is available, otherwise the standard \newcommand{\DTLfetchifnull}[7] is used (for compatibility with older documents).

With newer releases we essentially get

\NewDocumentCommand\DTLfetchifnull{mmmmmmm}
{%
  \begingroup%
    \protected@edef\@dtl@dogetrowforvalue{%
        \noexpand%
  \endgroup
  \noexpand\dtlgetrowindex{\noexpand\dtl@rowidx}{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
  }\@dtl@dogetrowforvalue%
  \ifx \dtl@rowidx\dtlnovalue%
    \expandafter\@secondoftwo%
  \else%
    \expandafter\@firstoftwo%
  \fi%
  {%
    \edtlgetrowforvalue{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
    \dtlgetentryfromcurrentrow{\dtlcurrentvalue}{\dtlcolumnindex{#1}{#4}}%
    \DTLifnull{\dtlcurrentvalue}{#6}{#5}% 
  }{%
    #7%
  }%
}%

that shows not really good programming style. For instance, there are several useless %, but they're not harmful, just annoying. Also the first \noexpand is useless, because `\endgroup is not expandable. I'd write it as

\NewDocumentCommand\DTLfetchifnull{mmmmmmm}
{%
  \begingroup
    \protected@edef\@dtl@dogetrowforvalue{%
  \endgroup
  \noexpand\dtlgetrowindex{\noexpand\dtl@rowidx}{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
  }\@dtl@dogetrowforvalue
  \ifx \dtl@rowidx\dtlnovalue
    \expandafter\@secondoftwo
  \else
    \expandafter\@firstoftwo
  \fi
  {%
    \edtlgetrowforvalue{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
    \dtlgetentryfromcurrentrow{\dtlcurrentvalue}{\dtlcolumnindex{#1}{#4}}%
    \DTLifnull{\dtlcurrentvalue}{#6}{#5}% 
  }{%
    #7%
  }%
}

If an older LaTeX release, the first line would be \newcommand{\DTLfetchifnull}[7].

The idea is to define \@dtl@dogetrowforvalue to yield

\endgroup
\dtlgetrowindex{\dtl@rowidx}
  {expansion of #1}
  {expansion of \dtlcolumnindex{#1}{#2}}
  {expansion of #3}

and then the scratch macro \@dtl@dogetrowforvalue will be executed, which will assign a meaning to \dtl@rowidx, which can be \dtlnovalue (depending on the arguments #1, #2 and #3. The initial \endgroup will balance the already digested \begingroup and the definition of \@dtl@dogetrowforvalue will be forgotten as soon as \endgroup is executed (but the expansion of \@dtl@dogetrowforvalue has already been placed on the input stream, so there is no problem with undefining it).

Next the token is compared with \dtlnovalue. The construction

\if<something>
  \expandafter\@secondoftwo
\else
  \expandafter\@firstoftwo
\fi
{tokens A}
{tokens B}

is very common in LaTeX programming. If the test returns true, tokens B will be used, otherwise tokens A.

So, if \dtl@rowidx turns out to be \dtlnovalue, the argument #7 will be used. Otherwise it will be some legal row index and

\edtlgetrowforvalue{#1}{\dtlcolumnindex{#1}{#2}}{#3}%
\dtlgetentryfromcurrentrow{\dtlcurrentvalue}{\dtlcolumnindex{#1}{#4}}%
\DTLifnull{\dtlcurrentvalue}{#6}{#5}% 

will be used. Finding out what this does needs studying the manual of datatool.