2

I guess my problem is related to Commands from xparse inside functions created by \cs_new:Npn? but I still don't understand what's going on: in the following WE (not minimal, sorry for this), why \IfNoValueF doesn't detect a -NoValue-?

\documentclass{article}
\usepackage[margin=0pt,paperheight=5cm]{geometry}
\usepackage{xparse}
\usepackage{datatool}

\ExplSyntaxOn

\DTLnewdb{instrumentsdb}

\cs_new_protected:Nn \mypkg_add_instrument:nnn
{%
  \DTLnewrow{instrumentsdb}%
  \DTLnewdbentry{instrumentsdb}{status}{#1}%
  \DTLnewdbentry{instrumentsdb}{name}{#2}%
  \DTLnewdbentry{instrumentsdb}{family}{#3}%
}

\NewDocumentCommand \brass {om}
{
  \mypkg_add_instrument:nnn {#1}{#2}{brass}%
}

\NewDocumentCommand \strings {om}
{
  \mypkg_add_instrument:nnn {#1}{#2}{strings}
}

\cs_new_protected:Nn \mypkg_display_instrument:nn
{%
  #2%
  \IfNoValueF {#1} {\\(#1)~(should~appear~only~if~"NoValue"~is~false) }%
}

\NewDocumentCommand \displayinstruments {}
{%
  \begin{description}
  \item[Brass]\
    \begin{itemize}
      \DTLforeach*[\DTLiseq{\tl_mypkg_family}{brass}]{instrumentsdb}{%
        \tl_mypkg_status=status%
        ,\tl_mypkg_name=name%
        ,\tl_mypkg_family=family%
      }{%
      \item\mypkg_display_instrument:nn {\tl_mypkg_status}{\tl_mypkg_name}%
      }
    \end{itemize}
  %
  \item[Strings]\
    \begin{itemize}
      \DTLforeach*[\DTLiseq{\tl_mypkg_family}{strings}]{instrumentsdb}{%
        \tl_mypkg_status=status%
        ,\tl_mypkg_name=name%
        ,\tl_mypkg_family=family%
      }{%
      \item\mypkg_display_instrument:nn {\tl_mypkg_status}{\tl_mypkg_name}%
      }
    \end{itemize}
  \end{description}
}
\ExplSyntaxOff
%
\begin{document}
\brass[new]{trumpet}
\brass[used]{trombone}
%
\strings[used]{guitar}
\strings{violin}
%
\displayinstruments
\end{document}

enter image description here

In https://tex.stackexchange.com/a/94827/18401:

  • it is advised to test for \NoValue in the interface conversion, that is within the definition of the document-level command. But, as hopefully pointed out in my example, it would lead to code duplication: one could imagine more than \brass and \strings document-level commands that need to test for \NoValue.
  • an alternative is proposed:

    \NewDocumentCommand { \foo } { O { } m }
      { \mypkg_foo:nn {#1} {#2} }
    

    where as we don't care if #1 is present or not we simply set an empty default. But its "emptyness" cannot be tested neither, as pointed out by the following similar WE.


\documentclass{article}
\usepackage[margin=0pt,paperheight=10cm]{geometry}
\usepackage{xparse}
\usepackage{datatool}

\ExplSyntaxOn

\DTLnewdb{instrumentsdb}

\cs_new_protected:Nn \mypkg_add_instrument:nnn
{%
  \DTLnewrow{instrumentsdb}%
  \DTLnewdbentry{instrumentsdb}{status}{#1}%
  \DTLnewdbentry{instrumentsdb}{name}{#2}%
  \DTLnewdbentry{instrumentsdb}{family}{#3}%
}

\NewDocumentCommand \brass {O{}m}
{
  \mypkg_add_instrument:nnn {#1}{#2}{brass}%
}

\NewDocumentCommand \strings {O{}m}
{
  \mypkg_add_instrument:nnn {#1}{#2}{strings}
}

\cs_new_protected:Nn \mypkg_display_instrument:nn
{%
  #2%
  \IfNoValueF {#1} {\\(#1)~(should~appear~only~if~"NoValue"~is~false) }%
  \tl_if_empty:nF {#1} { \\(#1)~(should~appear~only~if~false~"empty") }%
  \tl_if_blank:nF {#1} { \\(#1)~(should~appear~only~if~false~"blank") }%
}

\NewDocumentCommand \displayinstruments {}
{%
  \begin{description}
  \item[Brass]\
    \begin{itemize}
      \DTLforeach*[\DTLiseq{\tl_mypkg_family}{brass}]{instrumentsdb}{%
        \tl_mypkg_status=status%
        ,\tl_mypkg_name=name%
        ,\tl_mypkg_family=family%
      }{%
      \item\mypkg_display_instrument:nn {\tl_mypkg_status}{\tl_mypkg_name}%
      }
    \end{itemize}
  %
  \item[Strings]\
    \begin{itemize}
      \DTLforeach*[\DTLiseq{\tl_mypkg_family}{strings}]{instrumentsdb}{%
        \tl_mypkg_status=status%
        ,\tl_mypkg_name=name%
        ,\tl_mypkg_family=family%
      }{%
      \item\mypkg_display_instrument:nn {\tl_mypkg_status}{\tl_mypkg_name}%
      }
    \end{itemize}
  \end{description}
}
\ExplSyntaxOff
%
\begin{document}
\brass[new]{trumpet}
% \brass[used]{trombone}
%
\strings[used]{guitar}
\strings{violin}
%
\displayinstruments
\end{document}

enter image description here

  • I think it was asked to the team to add a function like \IfEmptyTF or \IfBlankTF to xparse. – Manuel Nov 27 '14 at 13:53
  • 1
    @Manuel 'blank' and 'empty' are not really interface things in the way an optional argument being missing is: that's why we have \IfNoValue(TF) but leave other tests to the code level. – Joseph Wright Nov 27 '14 at 13:55
  • 2
    @JosephWright I do miss “blank” anyways :) In any case, what I think is wrong here is using \IfValueTF outside \NewDocumentCommand. – Manuel Nov 27 '14 at 14:00
  • 2
    @Manuel One of the reasons \IfNoVlaue(TF) is expandable is that some people felt it should be usable 'lower down'. However, I'd concur with you: I feel it's a strictly interface thing and shouldn't propagate. – Joseph Wright Nov 27 '14 at 14:05
  • 1
    @JosephWright Because \IfNoValue(TF) is a pure xparse test, designed only for testing if optional arguments of xparse commands are present or not, I understand now and agree with you: it shouldn't propagate outside xparse commands. Anyway, AFAICS, it is not the first time LaTeX3 users (at least beginners like me) are bitten by the lack of possibility to run such tests at the code level... – Denis Bitouzé Nov 27 '14 at 17:23
3

To understand what is wrong with your code, I'd add a \tl_show:n

\cs_new_protected:Nn \mypkg_display_instrument:nn
  {
    \tl_show:n {#1}
    #2
    \IfNoValueF {#1} {\\(#1)~(should~appear~only~if~"NoValue"~is~false) }
  }

That will reveal that you are not getting -NoValue- as #1: you are getting \tl_mypkg_status. That's exactly what you'd expect from

\mypkg_display_instrument:nn { \tl_mypkg_status } { \tl_mypkg_name }

Probably you are looking to pass the values these variables hold

\mypkg_display_instrument:VV \tl_mypkg_status \tl_mypkg_name

with

\cs_generate_variant:Nn \mypkg_display_instrument:nn { VV }

Note however that I'd be very wary of storing -NoValue- in the way you have: it's intended as a document interface marker which should be converted internally into a more appropriate form. For example, as you are storing the fact that something is missing, I'd be much more inclined not to store it at all and pick this up on recovery.

| improve this answer | |
  • Passing the values these variables hold work like a charm! But I don't see your point when you say you'd "be much more inclined not to store it at all and pick this up on recovery": do you mean duplicate the \IfNoValueF {#1} {...} in each document-level command? – Denis Bitouzé Nov 27 '14 at 17:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.