2

I have a command, \Baz, that takes one optional argument which must be inline code. It relies on an external variable to store the default inline code (1). As standalone it works, but it does not meet the requirement of the project I am developing (2). That's where \Foo and a meta command to define it (3), enter the picture. Problem: when I try to define \Foo, I get the error: Defaults of '\Foo' have circular dependency.

Side notes: (1) The reason for keeping the default outside is to be able to modify \Baz that way. (2) Serialize calls to \Baz: information about the default is lost. (3) The tentative solution is borrowed from that of a post asking to create a lambda function.

\documentclass{report}
\usepackage{xparse}

\ExplSyntaxOn

\cs_set:Npn \__erw_baz_default:n #1{Hello, #1!}
\cs_set:Npn \__erw_baz_set:n #1
{
  \cs_gset:Npn \__erw_baz:n ##1 {#1}
}
\DeclareDocumentCommand{\Baz}
{O{\__erw_baz_default:n{##1}}}
{
  \__erw_baz_set:n {#1}
  \__erw_baz:n{world}
}

\cs_set:Npn \__erw_foo_set:n #1
{
  \cs_gset:Npn \__erw_foo:n ##1 {#1}
}
\cs_new_protected:Npn \__erw_make_Foo:n
#1 
{
  \use:x
  {
    \exp_not:n{\DeclareDocumentCommand{\Foo}}
    {O{#1}}
  }
  {
    \__erw_foo_set:n{##1}
    \__erw_foo:n{world}
  }

}
\__erw_make_Foo:n{Hello, ##1!}

\NewDocumentCommand{\Test}
{}
{

  \noindent
  \Baz[Jello,~##1!]\\
  \Baz\\

  \Foo[Jello,~##1!]\\

  %\Foo
  % Expected:     % Hello, world!
  % Actual:       % LaTeX3 Error: Defaults of '\Foo' have circular dependency

}

\ExplSyntaxOff


\begin{document}

\Test

\end{document}

enter image description here

  • 1
    You are defining \Foo like \DeclareDocumentCommand{\Foo}{O{Hello,~#1}}{...}, then if the argument #1 is not given, the default value of #1 will depend on the value of #1... That doesn't make much sense (or more precisely, it loops, as you can see). Perhaps try \__erw_make_Foo:n{Hello, ####1!}... – Phelype Oleinik Mar 16 at 21:18
  • I expected \__erw_make_Foo:n{Hello, ##1!} would make Hello, ##1! the default for #1 in \Foo's argument specification. – Erwann Mar 16 at 21:23
  • Yeah, \__erw_make_Foo:n{Hello, ####1!} worked. – Erwann Mar 16 at 21:29
1

The TeX-equivalent for “Have you tried turning it off and on again?” is “have you tried doubling or halving the hashes?”


When \__erw_make_Foo:n {Hello, ##1!} expands and the argument is consumed, the hashes are halved when TeX replaces the parameters of \__erw_make_Foo:n into its definition, so you have:

\DeclareDocumentCommand \Foo { O{Hello, #1!} }
  {
    \__erw_foo_set:n {#1}
    \__erw_foo:n { world }
  }

which as xparse advertises is a circular definition. If the optional argument is not given, then the default value of #1 depends on the value of #1, which depends on the value of #1, which depends...

If you double the hashes when calling \__erw_make_Foo:n then you get:

\DeclareDocumentCommand \Foo { O{Hello, ##1!} }
  {
    \__erw_foo_set:n {#1}
    \__erw_foo:n { world }
  }

which in turn inserts a parameter #1 when you use \__erw_foo_set:n.


A few nicpicks on your expl3:

  • Macros which are not supposed to be expandable (for instance, if they perform an assignment) must be defined with protected, so \cs_new_protected:Npn instead of \cs_new:Npn. The version without protected is strictly for expandable (by design) macros;

  • Avoid \cs_set(_protected):Npn unless you really need that. One of the advantages of (properly used) expl3 is that you avoid the name clashes with other packages and accidental overwriting of macros. If you use \cs_set:Npn you are walking over that. Use \cs_new(_protected):Npn;

  • \exp_args:NNx \DeclareDocumentCommand \Foo { O{#1} } is a bit easier than \use:x { \exp_not:n { \DeclareDocumentCommand \Foo } { O{#1} } };

  • Indent the code :-)

Here's the code:

\documentclass{report}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Npn \__erw_baz_default:n #1 { Hello,~#1! }
\cs_new_protected:Npn \__erw_baz_set:n #1
  { \cs_gset:Npn \__erw_baz:n ##1 {#1} }
\NewDocumentCommand \Baz { O{ \__erw_baz_default:n {##1} } }
  {
    \__erw_baz_set:n {#1}
    \__erw_baz:n { world }
  }

\cs_new_protected:Npn \__erw_foo_set:n #1
  { \cs_gset:Npn \__erw_foo:n ##1 {#1} }
\cs_new_protected:Npn \__erw_make_Foo:n #1
  {
    \exp_args:NNx \DeclareDocumentCommand \Foo { O{#1} }
      {
        \__erw_foo_set:n {##1}
        \__erw_foo:n { world }
      }
  }
\NewDocumentCommand \Test { }
  {
    \noindent
    \Baz[Jello,~##1!]\\
    \Baz\\
    \Foo[Jello,~##1!]\\
    \Foo
  }

\__erw_make_Foo:n {Hello, ####1!}
\ExplSyntaxOff
\begin{document}
\Test
\end{document}
| improve this answer | |

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.