As Phelype Oleinik suggested you could use an empty first optional argument and assign the default if it is empty. I'd do that with an argument processor:
\ExplSyntaxOn
\cs_new_protected:Npn \__bdmmxvii_Oarg_proc:nn #1 #2
{
\tl_if_empty:nTF { #2 }
{ \cs_set:Npn \ProcessedArgument { #1 } }
{ \cs_set:Npn \ProcessedArgument { #2 } }
}
\NewDocumentCommand \twooptsA { >{ \__bdmmxvii_Oarg_proc:nn { 0 } }O{} O{0} }
{
myB:~#1,~#2
}
\ExplSyntaxOff
Alternatively my suggestion with the star:
\ExplSyntaxOn
\NewDocumentCommand \twooptsB { O{0} s O{0} }
{
myB:~#1,~#3
}
\ExplSyntaxOff
And as a third option a key=value syntax:
\ExplSyntaxOn
\keys_define:nn { bdmmxvii }
{
,1 .tl_set:N = \__bdmmxvii_arg_a_tl
,1 .initial:n = 0
,2 .tl_set:N = \__bdmmxvii_arg_b_tl
,2 .initial:n = 0
}
\NewDocumentCommand \twooptsC { O{} }
{
\group_begin:
\keys_set:nn { bdmmxvii } { #1 }
\__bdmmxvii_twooptsC:VV \__bdmmxvii_arg_a_tl \__bdmmxvii_arg_b_tl
\group_end:
}
\cs_new:Npn \__bdmmxvii_twooptsC:nn #1 #2
{
myB:~#1,~#2
}
\cs_generate_variant:Nn \__bdmmxvii_twooptsC:nn { VV }
\ExplSyntaxOff
Everything together in a single document:
\documentclass[]{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected:Npn \__bdmmxvii_Oarg_proc:nn #1 #2
{
\tl_if_empty:nTF { #2 }
{ \cs_set:Npn \ProcessedArgument { #1 } }
{ \cs_set:Npn \ProcessedArgument { #2 } }
}
\NewDocumentCommand \twooptsA { >{ \__bdmmxvii_Oarg_proc:nn { 0 } }O{} O{0} }
{
myB:~#1,~#2
}
\NewDocumentCommand \twooptsB { O{0} s O{0} }
{
myB:~#1,~#3
}
\keys_define:nn { bdmmxvii }
{
,1 .tl_set:N = \__bdmmxvii_arg_a_tl
,1 .initial:n = 0
,2 .tl_set:N = \__bdmmxvii_arg_b_tl
,2 .initial:n = 0
}
\NewDocumentCommand \twooptsC { O{} }
{
\group_begin:
\keys_set:nn { bdmmxvii } { #1 }
\__bdmmxvii_twooptsC:VV \__bdmmxvii_arg_a_tl \__bdmmxvii_arg_b_tl
\group_end:
}
\cs_new:Npn \__bdmmxvii_twooptsC:nn #1 #2
{
myB:~#1,~#2
}
\cs_generate_variant:Nn \__bdmmxvii_twooptsC:nn { VV }
\ExplSyntaxOff
\begin{document}
\twooptsA\ \twooptsA[1][2] \twooptsA[1] \twooptsA[][2]
\twooptsB\ \twooptsB[1][2] \twooptsB[1] \twooptsB*[2]
\twooptsC\ \twooptsC[1=1,2=2] \twooptsC[1=1] \twooptsC[2=2]
\end{document}
Everything results in the same:

\tl_if_empty:nTF{#1}{myB:~0,~#2}{myB:~#1,~#2}. – Phelype Oleinik May 12 '19 at 18:23~before args? – bp2017 May 12 '19 at 18:28\ExplSyntaxOna normal space is ignored and~will be a normal space instead. I guess he wrote that out of habit (or to be more precise, you'll need\ExplSyntaxOnto use\tl_if_empty:nTF). – Skillmon May 12 '19 at 18:29\IfNoValueTFchecks only for-NoValue-. It won't work for[][6]because#1will be empty. – Phelype Oleinik May 12 '19 at 18:30O{0} s O{0}and then you can use\foo*[5]to only set the second one. – Skillmon May 12 '19 at 18:31\IfNoValuewould work :-) – Phelype Oleinik May 12 '19 at 18:32D(){0} O{0}, too. Then\foo(5)would only set the first argument,\foo[5]the second, and\foo(5)[5]both. – Skillmon May 12 '19 at 18:34-NoValue-flag wouldn't ever be passed to an argument processor. – Skillmon May 12 '19 at 18:36l3keysmodule (or any other key-value package), but the#2=foosyntax doesn't look like a good idea. – Skillmon May 12 '19 at 18:40\NewDocumentCommand{\myB}{E{12}{{0}{0}}}{myB: #1, #2}and use\myB2{6}. Problem. Solved. :D – Phelype Oleinik May 12 '19 at 18:41\NewDocumentCommand \foo { O{0} } { myB:~#1,~\baz } \NewDocumentCommand \baz { O{0} } { #1 }?! Or do you mean that you need to pass optional arguments on to another macro like\NewDocumentCommand \foo { o o } { \IfNoValueTF { #2 } { \IfNoValueTF { #1 } { \baz } { \baz [ { #1 } ] } } { \baz [ { #1 } ] [ { #2 } ] } }– Skillmon May 12 '19 at 19:00\cs_new_protected:Npn \__bdmmxvii_Oarg_pass_proc:n #1 { \tl_if_empty:nTF { #1 } { \cs_set:Npn \ProcessedArgument {} } { \cs_set:Npn \ProcessedArgument { [ { #1 } ] } } } \NewDocumentCommand \foo { >{ \__bdmmxvii_Oarg_pass_proc:n } O{} O{0} } { \baz #1 { #2 } }if the first argument of\bazis optional and the second mandatory. – Skillmon May 12 '19 at 19:10