# How to define/iterate over nested property lists in LaTeX3

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:

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

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

# 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}

\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}


\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