# How to define macros with xstring and pass them to other macros?

Is it possible to use the xstring package to pass the result of one macro into the definition of another macro?

If it is not possible, is there another way to accomplish this with LaTeX?

Specifically, why does this minimal working example work:

\documentclass{article}    \usepackage{mathrsfs}    \usepackage{xstring}

\newenvironment{axioms}[2]{

\StrMid{#1}{0}{1}[\firstletter]
\firstletter

\newcommand{\axiom}[1]{   \item[##1] }

#2 -- the \textbf{#1 Axioms}

\begin{itemize} }{\end{itemize}}

\title{}    \author{}    \date{}    \begin{document}

\begin{axioms}{Matroid}{$\mathscr{M}$}
\axiom{1}
\end{axioms}

\end{document}


and why does this minimal non-working example not work? (The difference is in the \newcommand line.)

\documentclass{article}    \usepackage{mathrsfs    \usepackage{xstring}

\newenvironment{axioms}[2]{

\StrMid{#1}{0}{1}[\firstletter]

\newcommand{\axiom}[1]{ \firstletter   \item[##1] }

#2 -- the \textbf{#1 Axioms}

\begin{itemize} }{\end{itemize}}

\title{}    \author{}    \date{}    \begin{document}

\begin{axioms}{Matroid}{$\mathscr{M}$}
\axiom{1}
\end{axioms}

\end{document}


It occurred to me that there is usually a lot of redundancy in my LaTeX code, and that I hold myself to much lower quality standards than with other languages. In order to begin changing this I have tried to use more (nested) macros, but with mixed success.

Page 13 of the documentation for xstring says:

The macros of this package are not purely expandable, i.e. they cannot be put in the argument of an \edef. Nestling macros is not possible neither. For this reason, all the macros returning a result (i.e. all excepted the tests) have an optional argument in last position. The syntax is [〈name〉], where 〈name〉 is the name of the control sequence that will receive the result of the macro: the assignment is made with an \edef which make the result of the macro 〈name〉 purely expandable.

Keeping this in mind, I tried to use the result of an xstring command as part of the definition of a \newcommand within a \newenvironment by using the [\result] thing mentioned in the documentation. However, I could not make this work, and got the error:

Something's wrong--perhaps a missing \item. \axiom{1}


I am also looking at pp. 17-19 of the same documentation for a possible solution, although right now I don't understand them enough. If I can decipher them and figure out something useful I will try to remember to post something.

If one of these questions (1)(2)(3)(4) answers my question, can you please explain in a little more detail how? I have read them but don't understand how to apply their answers to my situation. Specifically I want to capture only the first letter of an input and then use that first letter as an argument for another macro, not to create another macro.

It's not entirely clear to me what you are trying to do but the error occurs because your code produces the following:

\begin{itemize}
M \item
\end{itemize}


This is clearly problematic as the first thing in an itemize environment should be \item. I think that what you want is:

\documentclass{article}
\usepackage{xstring}

\newenvironment{axioms}[2]{
\StrMid{#1}{0}{1}[\firstletter]
\firstletter

\newcommand{\axiom}[1]{   \item[\firstletter##1] }

#2 -- the \textbf{#1 Axioms}

\begin{itemize}
}{\end{itemize}}

\begin{document}

\begin{axioms}{Matroid}{$\mathscr{M}$}
\axiom{1}
\end{axioms}

\end{document}


I have cut this down a little so that it is closer to a minimal working example.

• Sorry about that. I thought I needed mathrsfs for \mathscr{M} to load properly. – Chill2Macht Nov 4 '17 at 19:32

You can simplify the input by using enumitem: the idea is to extract the first letter and then use it in the label for enumerate.

\documentclass{article}
\usepackage{xstring}
\usepackage{enumitem}
\usepackage{mathrsfs}

\newenvironment{axioms}[2]
{%
% get the first letter
\StrMid{#1}{0}{1}[\firstaxiomletter]%
\par
#2 -- the \textbf{#1 Axioms}
% enumerate will use <first letter><number>
\begin{enumerate}[label=\firstaxiomletter\arabic*]
}
{\end{enumerate}}
\newcommand{\axiom}{\item}

\begin{document}

\begin{axioms}{Matroid}{$\mathscr{M}$}
\axiom $1+1=2$

\axiom $2+2=4$
\end{axioms}

\end{document}


An equivalent (but perhaps simpler) implementation with xparse and expl3:

\documentclass{article}
\usepackage{xparse}
\usepackage{enumitem}
\usepackage{mathrsfs}

% a user interface to an internal function
\ExplSyntaxOn
\NewExpandableDocumentCommand{\firstletter}{m}
{
}
\ExplSyntaxOff

\NewDocumentEnvironment{axioms}{mm}
{%
\par
#2 -- the \textbf{#1 Axioms}
\begin{enumerate}[label=\firstletter{#1}\arabic*]
}
{\end{enumerate}}
\newcommand{\axiom}{\item}

\begin{document}

\begin{axioms}{Matroid}{$\mathscr{M}$}
\axiom $1+1=2$

\axiom $2+2=4$
\end{axioms}

\end{document}

• What do the \par \addvspace{\topsep}  commands do? I might be loading too many packages, or doing something else wrong, but it doesn't seem to affect/change the output? Also, what allows the firstaxiomletter macro to be passed into the square brackets of \begin{enumerate}? Maybe it's just that using enumitem makes it possible to bypass using \newcommand, which perhaps was the only thing causing a problem? – Chill2Macht Nov 4 '17 at 19:31
• Also I really like this answer because it figured out what I wanted to do even though I didn't explain well in the question what I wanted to do, and it gave two approaches that perform that function with a better design. The other answer focuses more on the problems with the implementation, and this answer focuses more on the problems with the underlying design. Since my design problem is kind of narrow/specialist, I thought it might be more useful for other users of the site to highlight the problems with the implementation, which might still be relevant for other design problems. – Chill2Macht Nov 4 '17 at 19:40
• @Chill2Macht When the environment starts, the macro \firstaxiomletter is extracted; the inner enumerate uses this macro, however it is defined. – egreg Nov 4 '17 at 20:24