1

I have the following MWE:

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\begin{document}

\NewDocumentCommand\my:mapper:fna{mm}{
  \typeout{#1}
  \typeout{#2}

  \NewDocumentCommand\my:mapper:fnb{mm}{
    \typeout{#1}
    \typeout{#2}
  }

  \prop_map_function:NN \my:pl:b \my:mapper:fnb
}

\prop_new:N {\my:pl:a}
\prop_put:Nnn \my:pl:a {fooa} {baza}
\prop_put:Nnn \my:pl:a {foob} {bazb}
\prop_put:Nnn \my:pl:a {fooc} {bazc}

\prop_new:N {\my:pl:b}
\prop_put:Nnn \my:pl:b {xooa} {yaza}
\prop_put:Nnn \my:pl:b {xoob} {yazb}
\prop_put:Nnn \my:pl:b {xooc} {yazc}

\prop_map_function:NN \my:pl:a \my:mapper:fna

\end{document}

It errors at this point however:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! LaTeX error: "xparse/command-already-defined"
!
! Command '\my:mapper:fnb' already defined!
!
! See the LaTeX3 documentation for further information.
!
! For immediate help type H <return>.
!...............................................

l.35 \prop_map_function:NN \my:pl:a \my:mapper:fna

I am not sure what is wrong because the definition is created only one time just before the \prop_map_function call. Wondering how to fix this so I can do nested function calls where the nested function call can potentially use #1 and such from parent scopes.

Note, it does work if the command is moved outside of the nesting:

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\begin{document}

\NewDocumentCommand\my:mapper:fnb{mm}{
  \typeout{#1}
  \typeout{#2}
}

\NewDocumentCommand\my:mapper:fna{mm}{
  \typeout{#1}
  \typeout{#2}

  \prop_map_function:NN \my:pl:b \my:mapper:fnb
}

\prop_new:N {\my:pl:a}
\prop_put:Nnn \my:pl:a {fooa} {baza}
\prop_put:Nnn \my:pl:a {foob} {bazb}
\prop_put:Nnn \my:pl:a {fooc} {bazc}

\prop_new:N {\my:pl:b}
\prop_put:Nnn \my:pl:b {xooa} {yaza}
\prop_put:Nnn \my:pl:b {xoob} {yazb}
\prop_put:Nnn \my:pl:b {xooc} {yazc}

\prop_map_function:NN \my:pl:a \my:mapper:fna

\end{document}

But I would like to do it the original way if possible.

  • 2
    In case you are not aware, you are not using the naming conventions of expl3. – Manuel Mar 24 '18 at 18:02
3

What is wrong

  • You define a command you already defined. And \NewDocumentCommand is supposed to throw an error if a command is already defined. At this point you could use \DeclareDocumentCommand and would not encounter that problem.
  • You use #1 in the inner function (so that you will never use the content from property list b). To call arguments of a function that is defined within a function prepend a # for the level of nesting (so ##1 in this case).
  • You are abusing expl3 concepts like function signatures. A :N means that one token is expected (a control word), so you should not group it. In practice that means you should replace \prop_new:N {\my:pl:a} by \prop_new:N \my:pl:a.
  • You are introducing own naming conventions which make code kind of unreadable and bad style. The expl3 naming scheme for variables would be \<local or global>_[_]<module>_<variable>_<type>, e.g. \l_pollard_a_prop. For control sequences you would use \[__]<module>_<name>:<signature>, e.g. \pollard_mapper_fna:nn which has a valid signatures taking two groups.
  • You are mixing different levels of interface. The code level (expl3) syntax (completely different than intended) and the document level syntax (xparse). Your \NewDocumentCommands should probably be internal (i.e. \cs_new:Npn).

Why it is happening

A wrapper function is called on each item, but even after the first one the function is already defined. That means you are using \my:wrapper:fna on each item which tries to define \my:wrapper:fnb as a new command at each invocation. This throws the documented error.

The wrong output is caused by your usage of #1 instead of ##1. So you should replace this.

Would I recommend using \DeclareDocumentCommand?

No, because you are using a wrapper function on the entire property list b each time you call the wrapper for a.

Better code

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{xparse} % loads expl3

\ExplSyntaxOn
\prop_new:N \l_pollard_a_prop
\prop_new:N \l_pollard_b_prop
\cs_new:Npn \pollard_wrappera:nn #1#2
    {
      \noindent #1\\
      #2\\
      \cs_set:Npn \pollard_wrapperb:nn ##1##2
            {
                \noindent
                - ##1\\
            - ##2\par
            }
      \prop_map_function:NN \l_pollard_b_prop \pollard_wrapperb:nn
    }

\NewDocumentCommand { \puttopropa } { m m }
    {
        \prop_put:Nnn \l_pollard_a_prop { #1 } { #2 }
    }
\NewDocumentCommand { \puttopropb } { m m }
    {
        \prop_put:Nnn \l_pollard_b_prop { #1 } { #2 }
    }
\NewDocumentCommand { \processlista } { }
    {
        \prop_map_function:NN \l_pollard_a_prop \pollard_wrappera:nn
    }
\ExplSyntaxOff

\begin{document}
\puttopropa{fooa}{baza}
\puttopropa{foob}{bazb}
\puttopropa{fooc}{bazc}
\puttopropb{xooa}{yaza}
\puttopropb{xoob}{yazb}
\puttopropb{xooc}{yazc}
\processlista
\end{document}

Your code (compilable)

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\begin{document}

\NewDocumentCommand\my:mapper:fna{mm}{
  \typeout{#1}
  \typeout{#2}

  \DeclareDocumentCommand\my:mapper:fnb{mm}{
    \typeout{##1}
    \typeout{##2}
  }

  \prop_map_function:NN \my:pl:b \my:mapper:fnb

}

\prop_new:N {\my:pl:a}
\prop_put:Nnn \my:pl:a {fooa} {baza}
\prop_put:Nnn \my:pl:a {foob} {bazb}
\prop_put:Nnn \my:pl:a {fooc} {bazc}

\prop_new:N {\my:pl:b}
\prop_put:Nnn \my:pl:b {xooa} {yaza}
\prop_put:Nnn \my:pl:b {xoob} {yazb}
\prop_put:Nnn \my:pl:b {xooc} {yazc}

\prop_map_function:NN \my:pl:a \my:mapper:fna

\end{document}
  • Plus \prop_new:N {\my:pl:a}, plus \my:mapper:fnb using #1 #2 instead ##1 ##2, plus the naming conventions. – Manuel Mar 24 '18 at 18:05
  • In relation to your last point in the first section, I am confused as to why: tex.stackexchange.com/questions/422985/… – Lance Pollard Mar 24 '18 at 18:16
  • Thank you for pointing out the little details, it helps in learning how to do this. I am seeing just how fine-grained all the syntax is which I overlooked initially. – Lance Pollard Mar 24 '18 at 18:17
  • @LancePollard See my update. – TeXnician Mar 24 '18 at 18:22
  • @Manuel Did I miss anything? – TeXnician Mar 24 '18 at 18:22

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.