2

The following MWE is a very much simplified version of a more complicated system I am trying to develop. As a result, the interface seems a bit weird, and I don’t expect anyone to like it in the current form, nor do I myself. However it demonstrates some expansion issues I am dealing with.

Let us say that I want to create a macro system for any interface of the form

⟨symbol⟩ ^ ⟨upper index⟩ _ ⟨lower index⟩ ( ⟨argument⟩ )

For this purpose, I create the command \newmathfunctioncommand{#1} (with #1 being a command name without backslash). This defines a number of token lists to store the power, index, and argument in, as well as the command \#1 with the interface

\#1 { ⟨symbol⟩ } [ ⟨upper index⟩ ] [ ⟨lower index⟩ ] { ⟨argument⟩ (optional argument) } [ ⟨output math function⟩ ]

This is supposed to print the line ⟨symbol⟩ ^ ⟨upper index⟩ _ ⟨lower index⟩ ( ⟨argument⟩ ) as above. Then it takes an additional optional argument ⟨output math function⟩. This is supposed to contain the name of another math function command to wrap around the final value of the function \#1, so that this itself becomes a math function. In other words, it should allow interfaces like:

\newmathfunctioncommand{fun}

\newmathfunctioncommand{otherfun}

$\fun{f}[i][j]$ % Should output f^{i}_{j}

$\fun{f}[i][j]{x}$ % Should output f^{i}_{j}(x)

$\fun{f}[\fun{g}{x}]{t}$ % Should output f^{g(x)}(t)

$\fun{f}[i][j]{x}[otherfun]$ % Should output \otherfun{ f^{i}_{j}(x) }

$\fun{f}[i][j]{x}[otherfun]{t}$
    % Should output \otherfun{ f^{i}_{j}(x) }{t},
    % i.e., f^{i}_{j}(x)(t)

The purpose of this is to allow functions whose values are other functions, which is a well-known phenomenon in mathematics. However, this is the part of the system that I cannot get to work. The code that breaks is the one wrapped in the tag <code that does not work>. A piece of code that does work is in the tags <code that does work>. This working code implements everything except the output function system. Can someone fix this expansion issue?

Note that the reason for the use of \group_begin: ... \group_end: is to allow expressions like \fun{f}[\fun{g}{x}]{t} without causing infinite loops.

I am aware of some parts of the code which seem ridiculous in this MWE, like, output functions being set without a backslash. I ask you to keep this system, because they make much better sense in the more complicated setup I am really trying to create. Also, lots of stuff has been removed in this version of the code to keep it short, e.g., control of the size of parentheses.

\documentclass{article}

\usepackage{amsmath,xparse}

\ExplSyntaxOn

\tl_new:N\l_mathfunction_temp

\cs_set:Npn\g_mathfunction_identity:n#1 { #1 } % the identity function

\DeclareDocumentCommand\newmathfunctioncommand{m}
{
    % This defines the command \⟨#1⟩. First we define some token lists
    % to store the data of upper index, lower index etc. in.
    \tl_new:c { g_mathfunction_#1_upper_index }
    \tl_new:c { g_mathfunction_#1_lower_index }
    \tl_new:c { g_mathfunction_#1_argument }
    \tl_new:c { g_mathfunction_#1_output_function }
    \tl_set:cn { g_mathfunction_#1_output_function } { g_mathfunction_identity:n  }
        % This will be the default output function if none else is given
    \exp_args:Nc\DeclareDocumentCommand{#1}{moogo}{
        \group_begin:
            \IfNoValueTF { ##2 } % If ##2 is non-empty, set upper index token list
                                 % to that value 
            {
                % do nothing
            }
            {
                \tl_set:cn { g_mathfunction_#1_upper_index } { \exp_not:n { ##2 } }
            }
            \IfNoValueTF { ##3 } % If ##3 is non-empty, set lower index token list
                                 % to that value
            {
                % do nothing
            }
            {
                \tl_set:cn { g_mathfunction_#1_lower_index } { \exp_not:n { ##3 } }
            }
            \IfNoValueTF { ##4 } % If ##4 is non-empty, set argument token list
                                 % to that value
            {
                % do nothing
            }
            {
                \tl_set:cn { g_mathfunction_#1_argument } {
                    \exp_not:n { ##4 }
                }
            }
            \IfNoValueTF { ##5 } % If ##5 is non-empty, set output function
                                 % token list to that value.
            {
                % do nothing
            }
            {
                \tl_set:cn { g_mathfunction_#1_output_function } { \exp_not:n { ##5 } }
            }
            %
            %
            %
            \tl_set:Nx\l_mathfunction_temp  % Store output data in a temporary
                                            % token list
            {
                \exp_not:n { ##1 }
                \tl_if_empty:cTF { g_mathfunction_#1_upper_index }
                {
                    % do nothing
                }
                {
                    \sp { \use:c { g_mathfunction_#1_upper_index } }
                }
                \tl_if_empty:cTF { g_mathfunction_#1_lower_index }
                {
                    % do nothing
                }
                {
                    \sb { \use:c { g_mathfunction_#1_lower_index } }
                }
                \tl_if_empty:cTF { g_mathfunction_#1_argument }
                {
                    % do nothing
                }
                {
                    (\use:c { g_mathfunction_#1_argument } )
                }
            }
            %
            % Now if the argument has a value, and only in this case, set
            % the command \l_mathfunction_output_command:n to be equal
            % to the output math function command.
            %
            \tl_if_empty:cTF { g_mathfunction_#1_argument }
            {
                \cs_set_eq:NN
                    \l_mathfunction_output_command:n
                    \g_mathfunction_identity:n
            }
            {
                \exp_args:Nno\cs_set_eq:cc
                    { l_mathfunction_output_command:n }
                    { \use:c { g_mathfunction_#1_output_function } }
            }
        %
        %
        % Now finally it is time to render the actual output
        %
        % <code that does not work>
         \exp_args:NNo
         \exp_args:NNV
         \group_end:
         \l_mathfunction_output_command:n
         { \l_mathfunction_temp }
        % </code that does not work>
        %
        %
        % <code that does work>
        % \exp_args:NV
        % \group_end:
        % \l_mathfunction_temp
        % </code that does work>
    }
}

\DeclareDocumentCommand\mathfunctionsetup{moogo}
    % a command to setup the arguments
    % globally.
{
    \IfNoValueTF { #2 }
    {
        % do nothing
    }
    {
        \tl_set:cn { g_mathfunction_#1_upper_index } { \exp_not:n { #2 } }
    }
    \IfNoValueTF { #3 }
    {
        % do nothing
    }
    {
        \tl_set:cn { g_mathfunction_#1_lower_index } { \exp_not:n { #3 } }
    }
    \IfNoValueTF { #4 }
    {
        % do nothing
    }
    {
        \tl_set:cn { g_mathfunction_#1_argument } {
            \exp_not:n { #4 }
        }
    }
    \IfNoValueTF { #5 }
    {
        % do nothing
    }
    {
        \tl_set:cn { g_mathfunction_#1_output_function } { \exp_not:n { #5 } }
    }
}

\ExplSyntaxOff

\begin{document}

\newmathfunctioncommand{fun}

\newmathfunctioncommand{otherfun}

$\fun{f}[i][j]$ % Should output f^{i}_{j}

$\fun{f}[i][j]{x}$ % Should output f^{i}_{j}(x)

$\fun{f}[\fun{g}{x}]{t}$ % Should output f^{g(x)}(t) 

$\fun{f}[i][j]{x}[otherfun]$ % Should output \otherfun{ f^{i}_{j}(x) }

$\fun{f}[i][j]{x}[otherfun]{t}$
    % Should output \otherfun{ f^{i}_{j}(x) }{t},
    % i.e., f^{i}_{j}(x)(t)

\mathfunctionsetup{fun}[a][b][otherfun]

$\fun{g}{x}{t}$ % Should output \otherfun { g^{a}_{b}(x) } { t },
                % i.e., g^{i}_{j}(x)(t)

\mathfunctionsetup{fun}[a][b][fun]

$\fun{g}{x}[p][q]{r}$   % Should output \fun { g^a_b(x) }[p][q]{r},
                        % i.e., g^{a}_{b}(x) ^{p}_{q}(r) 
\end{document}
  • @DavidCarlisle I see your point in this case; however, with the problem I am really trying to solve, I do not really see any way around using it. As I have said many times in this post, this looks weird, but I can’t find any other way to provide a MWE for the actual issue I have. – Gaussler Feb 4 '18 at 10:40
  • @DavidCarlisle My actual system uses the syntax \function[keyvals]{argument}. It is important to be able to omit the argument and render only the function. And braces are useful because they take care of any issues in expressions like \function{\function{x}+1}. Otherwise, parentheses could also be used. However, as I said many times: The interface in the stated, simplified form is weird, yes, but the original version would make the MWE too long. I did not see any other way to formulate the question. The expansion issue I have is still pretty clear and concrete IMO. – Gaussler Feb 4 '18 at 11:06
  • I think what you want is a way to stop \fun{\fun{x}}{t} from looping. I think you can forget about expansion and just put a system to stop that into the definition: \IfValueTF{##4}{ <ensure it loops one more time> } { <ensure it DOESN'T loop again> } wouldn't that be enough? – Manuel Feb 4 '18 at 11:31
  • @Manuel, if I understand you right, then no. It is a more general problem: Imagine I apply \function[keyval]{\function{x},\function{y}}. This will first set up a \group_begin: ... \group_end:, and in this group, the general setup function is applied to keyval. It is then important that this keyval assignment does not affect \function{x} and \function{y}. – Gaussler Feb 4 '18 at 11:40
  • To share a bit of the actual syntax: The key upper sets the upper index, and the key arg sets the argument. Then \fun[upper=7]{x} is really just short for \fun[upper=7,arg=x]. Then I need to be able to say \fun[upper=\fun{t},arg=\fun{s}] without creating a loop. – Gaussler Feb 4 '18 at 11:43
2

May be it would be clearer to have syntax \fun{f}[a][b](x)?

In any case, here's a solution. The only change is that the optional function argument is at thebeginning \fun[otherfun]{f}[a][b]{t}{x} should be the way of giving functions.

Note that there are many things that could be corrected from your document (names of variables, names of functions, usage of TF, some unnecessary things regarding expansions, etc.).

The only thing this doesn't get right is the last output, since I don't know the logic behind it, I don't know how to ensure that the last sub- superscripts are only added in the correct situations. If you add the logic I can correct my code.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\tl_new:N \l_gaussler_tmp_tl

\NewDocumentCommand \newmathfunctioncommand { m }
 {
  \gaussler_newmfcmd:c { #1 }
 }
\NewDocumentCommand \mathfunctionsetup { m o o g o }
 {
  \IfValueT {#2} { \tl_set:cn { l_gaussler_mf_ \tl_to_str:n {#1} _sp_tl  } { #2 } }
  \IfValueT {#3} { \tl_set:cn { l_gaussler_mf_ \tl_to_str:n {#1} _sb_tl  } { #3 } }
  \IfValueT {#4} { \tl_set:cn { l_gaussler_mf_ \tl_to_str:n {#1} _arg_tl } { #4 } }
  \IfValueT {#5} { \cs_set:cpn { gaussler_mf_ \tl_to_str:n {#1} _use:w } { \use:c { #5 } } }
 }
\cs_new_protected:Npn \gaussler_newmfcmd:N #1
 {
  \tl_new:c { l_gaussler_mf_ \cs_to_str:N #1 _arg_tl }
  \tl_new:c { l_gaussler_mf_ \cs_to_str:N #1 _sp_tl }
  \tl_new:c { l_gaussler_mf_ \cs_to_str:N #1 _sb_tl }
  \cs_new:cpn { gaussler_mf_ \cs_to_str:N #1 _use:w } { \use:n }
  \NewDocumentCommand #1 { o m o o g }
   {
    \group_begin:
    \IfValueT {##1}
     { \cs_set:cpn { gaussler_mf_ \cs_to_str:N #1 _use:w } { \use:c { ##1 } } }
    \IfValueT {##3}
     { \tl_set:cn { l_gaussler_mf_ \cs_to_str:N #1 _sp_tl } { ##3 } }
    \IfValueT {##4}
     { \tl_set:cn { l_gaussler_mf_ \cs_to_str:N #1 _sb_tl } { ##4 } }
    \IfValueT {##5}
     { \tl_set:cn { l_gaussler_mf_ \cs_to_str:N #1 _arg_tl } { ##5 } }
    \tl_set:Nx \l_gaussler_tmp_tl
     {
      \IfNoValueTF {##5}
       { \exp_not:N \use:n }
       { \use:c { gaussler_mf_ \cs_to_str:N #1 _use:w } }
       {
        \exp_not:n { ##2 }
        \tl_if_empty:cF { l_gaussler_mf_ \cs_to_str:N #1 _sp_tl }
         { \sp{ \tl_use:c { l_gaussler_mf_ \cs_to_str:N #1 _sp_tl } } }
        \tl_if_empty:cF { l_gaussler_mf_ \cs_to_str:N #1 _sb_tl }
         { \sb{ \tl_use:c { l_gaussler_mf_ \cs_to_str:N #1 _sb_tl } } }
        \tl_if_empty:cF { l_gaussler_mf_ \cs_to_str:N #1 _arg_tl }
         { ( \tl_use:c { l_gaussler_mf_ \cs_to_str:N #1 _arg_tl } ) }
       }
     }
    \exp_last_unbraced:NV
    \group_end:
    \l_gaussler_tmp_tl
   }
 }
\cs_generate_variant:Nn \gaussler_newmfcmd:N { c }

\ExplSyntaxOff

\begin{document}

\newmathfunctioncommand{fun}

\newmathfunctioncommand{otherfun}

$\fun{f}[i][j]$ % Should output f^{i}_{j}

$\fun{f}[i][j]{x}$ % Should output f^{i}_{j}(x)

$\fun{f}[\fun{g}{x}]{t}$ % Should output f^{g(x)}(t) 

$\fun[otherfun]{f}[i][j]{x}$ % Should output \otherfun{ f^{i}_{j}(x) }

$\fun[otherfun]{f}[i][j]{x}{t}$
    % Should output \otherfun{ f^{i}_{j}(x) }{t},
    % i.e., f^{i}_{j}(x)(t)

\mathfunctionsetup{fun}[a][b][otherfun]

$\fun{g}{x}{t}$ % Should output \otherfun { g^{a}_{b}(x) } { t },
                % i.e., g^{i}_{j}(x)(t)

\mathfunctionsetup{fun}[a][b][fun]

$\fun{g}{x}[p][q]{r}$   % Should output \fun { g^a_b(x) }[p][q]{r},
                        % i.e., g^{a}_{b}(x) ^{p}_{q}(r) 
\end{document}
  • Thanks for the clarification :-). I thank you for your answer, but if you had read the question, you would have seen that I never doubted the fact that the syntax is weird. My actual problem is a much more complicated macro system where the input system is completely different (actually, keyval-based). There, it is important to be able to control fun and otherfun separately, so that you can change fun and otherfun and implement different keyval interfaces for these two commands. – Gaussler Feb 4 '18 at 10:35
  • Also, it is important to be able to control the choice of output function centrally, and to only apply it in the case where the function has an argument. If you look through my original code, you will see that my use of \exp_args:Nno \cs_set_eq:cc { ... } { ... } has good reasons. Finally, the expansion method you suggest in the last paragraph does not work when inserted into my original example. All in all, I do not see how your solution solves my actual problem. Thank you nevertheless. – Gaussler Feb 4 '18 at 10:35
  • EDIT: I see your point now, there was one case where I wrote \exp_args:Nno \cs_set_eq:cc where it was redundant. This was an error that occurred when writing this MWE. – Gaussler Feb 4 '18 at 10:38
  • One final remark: In my actual problem, it is of great importance to be able to apply changes to the token list storage commands and to do it locally. For instance, one may want a keyval command that adds something to the right of the subscript, but inserts a comma if the subscript already contains tokenst, like a_{i,j}. That is the reason why I do these redundant-seeming token list reassignments. – Gaussler Feb 4 '18 at 10:55
  • \exp_args:NNo \group_end: \l_mathfunction_output_command:n { \l_mathfunction_temp } does not solve the issue. I get that \l_mathfunction_output_command:n is an unknown control sequence. – Gaussler Feb 4 '18 at 10:58

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.