Following an earlier question I switched to xparse. Now I am sitting in a compatibility trap and try to allow old and new stuff at the same time. The point of origin is the infamous macro with two optional arguments. My minimal example is:



\NewDocumentCommand \sidenotetext { o o m } {%
(#1), (#2), #3

\sidenotetext{arg} % case (1)

\sidenotetext[1]{arg} % \sidenotetext[mark=1]{arg}; case 2

\sidenotetext[][]{arg} % case 3

\sidenotetext[][2]{arg} %\sidenotetext[offset=2]{arg}; case 4

\sidenotetext[3][]{arg} %\sidenotetext[mark=3]{arg}; case 5

\sidenotetext[4][5]{arg} %\sidenotetext[mark=4,offset=5]{arg}; case 6 

Is it possible to make the left and the right macros (commented out) work at the same time? If not, Is it possible at all to make case 2 work, i.e. no key always means mark and the rest is key value? I assume, l3keys is the right choice to play nice with xparse?

1 Answer 1


If the two optional arguments deal with incompatible types of argument, the only feasible way is to have one before and one after the mandatory argument, so

\NewDocumentCommand{\sidenotetext} { o m o }


\NewDocumentCommand{\sidenotetext} { O{default} m O{default} }

In this way you can call


However this is clumsy. Better going with a key-value approach.

\NewDocumentCommand{\sidenotetext}{ O{} m }
  \andy_sidenotetext:nn { #1 } { #2 }

\keys_define:nn { andy/sidenote }
  mark .tl_set:N = \l_andy_sidenote_mark_tl,
  offset .tl_set:N = \l_andy_sidenote_offset_tl,
  offset .initial = 0,

\cs_new_protected:Npn \andy_sidenotetext:nn #1 #2
  \keys_set:nn { andy/sidenote } { #1 }
  <code using \l_andy_sidenote_mark_tl, \l_andy_sidenote_offset_tl
   and #2 (the actual text)>

Making the two calls compatible with each other is something I wouldn't recommend: stick to one syntax. There are macros around that allow both approaches, but it's because they were defined when a key-value approach was unfeasible. An example is \section in Koma-Script classes, where the test for a key-value syntax is done on the basis whether a = appears in the optional argument, which is not very robust.

If what you want to do is redefining a legacy command, I would recommend using a different name.

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.