2

If one defines a command with a key-value argument, say \ket[<key=value>]{<stuff>}, one gets the wrong output when nesting it (in my opinion). E.g. \ket[delim = |)]{ \ket{\psi} } changes the delimiters of both kets, but should only do it for the outer one, if \ket is defined as

\NewDocumentCommand{\ket}{ o m }
{
\group_begin:
    \IfNoValueF{#1} { \mymodule_set_keys:n {#1} }
    \mymodule_ket:n {#2}
\group_end:
}

A quick solution would be to set the keys to their default values when the o-argument is not given as below.

\NewDocumentCommand{\ket}{ o m }
{
\group_begin:
    \IfNoValueTF{#1}
      { \mymodule_set_keys:n { delim } }
      { \mymodule_set_keys:n {#1} }
    \mymodule_ket:n {#2}
\group_end:
}

This solution wouldn't allow changes made by the user with \mymoduleset (I call this 'user default' in the MWE's comments below), say \mymoduleset[delim = \langle|].

The idea is that when the command \ket is defined, it should initially use the packages default values i.e. delim .default:n = {\lvert\rangle}, but when the user want a different output for all \ket's (globally in some sense), then he can change it with \mymoduleset, such that \ket{<stuff>} produces \langle <stuff> | when \mymoduleset[delim = \langle|] is used.

How can one achieve a solution, which

  • allows 'global' changes by the user with \mymoduleset
  • and local changes i.e. \ket[<key=value>]{<stuff>} doesn't affect inner \ket's when nestet i.g. \ket[delim = |)]{ \ket{\psi} }

MWE

\documentclass{article}
\usepackage{xparse}
\usepackage{amsmath}

\ExplSyntaxOn

\tl_new:N \l__mymodule_ket_delim_tl

\keys_define:nn { mymodule }
{
    delim .tl_set:N  = \l__mymodule_ket_delim_tl,
    delim .default:n = {\lvert\rangle}
}

\cs_new_protected:Npn \mymodule_ket:n #1
{
    \tl_item:Nn \l__mymodule_ket_delim_tl {1}
    #1
    \tl_item:Nn \l__mymodule_ket_delim_tl {2}
}

\cs_new_protected:Npn \mymodule_set_keys:n #1
{ \keys_set:nn { mymodule } {#1} }

\NewDocumentCommand{\ket}{ o m }
{
\group_begin:
    \IfNoValueTF{#1}
    { \mymodule_set_keys:n { delim } } % package default, but should be user default
    { \mymodule_set_keys:n {#1} }      % local change for this macro only

    \mymodule_ket:n {#2}
\group_end:
}

\NewDocumentCommand{\mymoduleset}{ o }
{
    \IfNoValueTF{#1}
    { \mymodule_set_keys:n { delim } } % Package default
    { \mymodule_set_keys:n {#1}      } % User default
}
\ExplSyntaxOff
\mymoduleset

\begin{document}
    \begin{align}
        \ket{\psi}                     &\quad \text{desired result: } | \psi \rangle \\
        \ket{ \ket{\psi} }             &\quad \text{desired result: } || \psi \rangle\rangle \\
        \ket[delim = |)]{ \ket{\psi} } &\quad \text{desired result: } || \psi \rangle ) \\
        \ket{ \ket[delim = |)]{\psi} } &\quad \text{desired result: } || \psi ) \rangle
    \end{align}

    \mymoduleset[delim = {\langle\rvert}]

    \begin{align}
        \ket{\psi}                     &\quad \text{desired result: } \langle \psi | \\
        \ket{ \ket{\psi} }             &\quad \text{desired result: } \langle\langle \psi || \\
        \ket[delim = (|]{ \ket{\psi} } &\quad \text{desired result: } ( \langle \psi || \\
        \ket{ \ket[delim = (|]{\psi} } &\quad \text{desired result: } \langle ( \psi ||
    \end{align}
\end{document}

enter image description here

Note: The MWE above is not the actual use case but illustrate the problem. The actual use case is too long to write here

  • Beyond 'That's not how grouping works in TeX', I'm not sure what an answer here can be – Joseph Wright Jan 27 at 21:10
3

You can use a meta key.

\documentclass{article}
\usepackage{xparse}
\usepackage{amsmath}

\ExplSyntaxOn

\tl_new:N \l__mymodule_ket_delim_tl

\keys_define:nn { mymodule }
 {
  delim   .tl_set:N = \l__mymodule_ket_delim_tl,
 }

\cs_new_protected:Npn \mymodule_ket:n #1
 {
  \tl_item:Nn \l__mymodule_ket_delim_tl {1}
  #1
  \tl_item:Nn \l__mymodule_ket_delim_tl {2}
 }

\cs_new_protected:Npn \mymodule_set_keys:n #1
 {
  \keys_set:nn { mymodule } {#1}
 }

\NewDocumentCommand{\ket}{ O{} m }
 {
  \group_begin:
  \mymodule_set_keys:n {default,#1} % local change for this macro only
  \mymodule_ket:n {#2}
  \group_end:
 }

\NewDocumentCommand{\mymoduleset}{m}
 {
  \keys_define:nn { mymodule }
   {
    default .meta:n = { #1 }
   }
 }

\ExplSyntaxOff

% initialize
\mymoduleset{delim=\lvert\rangle}

\begin{document}

\begin{align}
  \ket{\psi}                     &\quad \text{desired result: } | \psi \rangle \\
  \ket{ \ket{\psi} }             &\quad \text{desired result: } || \psi \rangle\rangle \\
  \ket[delim = |)]{ \ket{\psi} } &\quad \text{desired result: } || \psi \rangle ) \\
  \ket{ \ket[delim = |)]{\psi} } &\quad \text{desired result: } || \psi ) \rangle
\end{align}

\mymoduleset{delim = \langle\rvert}

\begin{align}
  \ket{\psi}                     &\quad \text{desired result: } \langle \psi | \\
  \ket{ \ket{\psi} }             &\quad \text{desired result: } \langle\langle \psi || \\
  \ket[delim = (|]{ \ket{\psi} } &\quad \text{desired result: } ( \langle \psi || \\
  \ket{ \ket[delim = (|]{\psi} } &\quad \text{desired result: } \langle ( \psi ||
\end{align}

\end{document}

enter image description here

| improve this answer | |
  • And here I thought you couldn't define keys that already existed (here default). Nice, shorter and less cumbersome solution than mine. – Simon Feb 1 at 20:30
2

As commented by Joseph, the solution I sought is beyond how grouping works in TeX. So I came up with a work-around solution, where I store the user default in a sequence, and it is used in \ket to set the keys to the user default. While I can't prevent 'local changes', i.e. \ket[<key=value>]{<stuff>}, from affecting inner kets, I can always use this sequence to override the local changes, so they effectively only apply to one command.

\documentclass{article}
\usepackage{xparse}
\usepackage{amsmath}

\ExplSyntaxOn

\tl_new:N \l__mymodule_ket_delim_tl
\seq_new:N \l__mymodule_user_default_seq

\seq_const_from_clist:Nn \l__mymodule_pkg_default_seq { delim }

\keys_define:nn { mymodule }
{
    delim .tl_set:N  = \l__mymodule_ket_delim_tl,
    delim .default:n = {\lvert\rangle}
}

\cs_new_protected:Npn \mymodule_ket:n #1
{
    \tl_item:Nn \l__mymodule_ket_delim_tl {1}
    #1
    \tl_item:Nn \l__mymodule_ket_delim_tl {2}
}

\cs_new_protected:Npn \mymodule_set_keys:n #1
{ \keys_set:nn { mymodule } {#1} }

\cs_new_protected:Npn \mymodule_new_ket:N #1
{
    \NewDocumentCommand{#1}{ o m }
    {
    \group_begin:
        % Use user default
        \exp_args:Nf \mymodule_set_keys:n { \seq_use:Nn \l__mymodule_user_default_seq {,} }
        \IfNoValueF{##1}
        {
            % local change
            \mymodule_set_keys:n {##1}
        }

        \mymodule_ket:n {##2}
    \group_end:
    }
}

\NewDocumentCommand{\NewKet}{ m o }
{
    \seq_set_eq:NN \l__mymodule_user_default_seq \l__mymodule_pkg_default_seq
    \IfNoValueTF{#2}
    {
        % Set to package default
        \mymodule_set_keys:n { delim }
    }
    {
        % Set to user default
        \mymodule_set_keys:n {#2}
        \seq_put_right:Nn \l__mymodule_user_default_seq {#2}
    }
    \mymodule_new_ket:N #1
}

\NewDocumentCommand{\mymoduleset}{ o }
{
    \IfNoValueTF{#1}
    {
        % Set to package default
        \mymodule_set_keys:n { delim }
        \seq_set_eq:NN \l__mymodule_user_default_seq \l__mymodule_pkg_default_seq
    }
    {
        % Set to user default
        \mymodule_set_keys:n {#1}
        \seq_put_right:Nn \l__mymodule_user_default_seq {#1}
    }
}

\ExplSyntaxOff

\NewKet{\ket}

\begin{document}
    \begin{align}
        \ket{\psi}                     &\quad \text{desired result: } | \psi \rangle \\
        \ket{ \ket{\psi} }             &\quad \text{desired result: } || \psi \rangle\rangle \\
        \ket[delim = |)]{ \ket{\psi} } &\quad \text{desired result: } || \psi \rangle ) \\
        \ket{ \ket[delim = |)]{\psi} } &\quad \text{desired result: } || \psi ) \rangle
    \end{align}

    \mymoduleset[delim = {\langle\rvert}]

    \begin{align}
        \ket{\psi}                     &\quad \text{desired result: } \langle \psi | \\
        \ket{ \ket{\psi} }             &\quad \text{desired result: } \langle\langle \psi || \\
        \ket[delim = (|]{ \ket{\psi} } &\quad \text{desired result: } ( \langle \psi || \\
        \ket{ \ket[delim = (|]{\psi} } &\quad \text{desired result: } \langle ( \psi ||
    \end{align}
\end{document}

This gives the desired result

enter image description here

| 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.