Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

In \DeclareDocumentCommand if I have two successive optional arguments how can I signal the first one is missing?

For example:

\documentclass{article}
\usepackage{xparse}
\begin{document}

\ExplSyntaxOn   
\DeclareDocumentCommand{\test}{o o}
{
\IfValueTF{#1}
    {#1}{#2}
}
\ExplSyntaxOff  

\test[][1]
\end{document}

produces nothing and I would like it to produce 1.

share|improve this question

2 Answers 2

up vote 4 down vote accepted

There is a difference between passing nothing an not using it. In fact, your test \IfValueTF{#1} evaluates to true, as #1 has a value even though this value may be empty.

To test for passing an empty argument (in other words, passing nothing), use some techniques from How to check if a macro value is empty or will not create text with plain TeX conditionals?:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\DeclareDocumentCommand{\test}{o o}
{
  \if\relax\detokenize{#1}\relax
    #2%
  \else
    #1%
  \fi
}
\ExplSyntaxOff  

\begin{document}
\test[][1]
\end{document}

or use a true LaTeX3 check:

\ExplSyntaxOn
\DeclareDocumentCommand{\test}{o o}
{
  \tl_if_blank:nTF { #1 }
    { #2 }
    { #1 }
}
\ExplSyntaxOff  

Note that a user-interface where you have 2 optional arguments using the same delimiter is confusion. The only way to pass the second argument is to specify the first as well, making it not really optional. For this xparse provides the means to adjust the argument delimiters:

\ExplSyntaxOn
\DeclareDocumentCommand{\test}{o d()}
{
  \IfValueTF{#1}
    {#1}{#2}
}
\ExplSyntaxOff  

The above definition of \test[<first>](<second>) has two optional arguments. The first uses [] while the second uses (), making a clear distinction between their usages and there fully optional, as in \test(1).

For more complex (multiple) arguments it's often advised to create a key-value interface.

share|improve this answer
1  
I was half-way writing something similar (I'd probably use \tl_if_blank:nTF for the test in the first case). Perhaps worth noting that for complex optional arguments, it's usually considered a good idea to consider a keyval interface. (Of course, that does depend on the real use case.) –  Joseph Wright Jul 8 at 6:06
    
@Werner: Thanks! This is exactly what I was looking for. –  Nate Ackerman Jul 8 at 6:22

There are some commands in the LaTeX kernel that allow two or three optional arguments in a row:

\makebox[<length>][<alignment>]{<text>}

\parbox[<outer alignment>][<height>][<inner alignment>]{<width>}{<text>}

(note that minipage accepts the same arguments as \parbox, without the last one, of course). In both cases, the optional arguments after the first are meaningful only if the preceding one appears. For instance, the <alignment> option to \makebox makes sense only if a <width> has been specified. The case of \parbox is slightly different, but the first optional argument is used also for the third, if this is not specified; probably a better syntax would have been

\parbox[<outer alignment>]{<width>}[<height>][<inner alignment>]{<text>}

so as to never require an optional argument.

The \cite command of biblatex (together with variations thereof) accepts two optional arguments:

\cite[<postnote>]{<key>}
\cite[<prenote>][<postnote>]{<key>}

Here an empty <postnote> must be specified if only a <prenote> is wanted as in

\cite[See][]{mykey}

In amsref an alternative syntax was proposed: instead of \cite[<postnote>]{<key>}, for the typical case in which one wants

[3, Lemma 4]

traditionally input as \cite[Lemma~4]{mykey}, the package provides

\cite{mykey}*{Lemma~4}

I find this quite interesting, but it doesn't conform to the standard practices. Note that this would allow (but it doesn't, actually) a syntax such as

\cite[<prenote>]{key}*{<postnote>}

that would probably be even more readable.

Building a good user interface is not an easy task, as you see. But one has to think deeply whenever it appears that an optional argument becomes almost mandatory.

In these cases a good alternative is a key-value interface; the example of \parbox might be

\PARBOX[
  outer=<character>,% c (default), t or b
  inner=<character>,% default=outer, c, t, b or s
  height=<length>,
]{<width>}{<text>}

and it would be easy to make the outer and inner options to accept also longer values such as top, bottom, center or spread.

A key-value interface should be preferred whenever the options are independent of each other.

This said, you can augment the facilities of xparse by defining a new “check command”

\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\IfBlankTF}{mmm}
 {
  \tl_if_blank:nTF { #1 } { #2 } { #3 }
 }
\ExplSyntaxOff

so that you can later define your command as

\NewDocumentCommand{\test}{o o}
 {%
  \IfNoValueTF{#1}
   {%
    Something for the case of no optional argument%
   }%
   {%
    \IfNoValueTF{#2}
     {%
      Something with #1 for the case of just one optional argument%
     }%
     {%
      \IfBlankTF{#1}
       {%
        Something with #2 for the case \string\test[][...]%
       }%
       {%
        Something with #1 and #2%
       }%
     }%
   }%
 }
share|improve this answer
    
Useful analysis: as you say, most of the kernel cases are 'good', although perhaps some revision here and there would be beneficial. (The balance between multiple optional args and one keyval one is not always easy: the latter has a certain appeal but often in 'real' documents well-designed optional arguments are actually more user-friendly.) –  Joseph Wright Jul 8 at 9:56

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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