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:n = 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.

  • The answer is great! I just started using the xparse package. I am kindly wondering if a :n should be added to offset .initial = 0,? Commented Oct 8, 2022 at 20:42
  • @M.AlJumaily Sure! Let me fix it. I guess that at the time the interface wasn't yet finalized.
    – egreg
    Commented Oct 8, 2022 at 20:44
  • Thank you for the quick response and update :)! Commented Oct 8, 2022 at 20:45
  • You could just exploit the unknown interface in l3keys.
    – Gaussler
    Commented Oct 9, 2022 at 7:13

You must log in to answer this question.

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