[Tex/LaTex] Why does cleveref’s \crefname command have no effect when it appears within a \foreach

cleverefcross-referencingforeach

This question follows on from this one, which I asked a few weeks ago. I have since discovered the cleveref package, and I would like to use it for ease in referring to theorem-like environments without having to remember whether a given statement was a theorem, a lemma, a proposition, etc.

Here is a small example document. When I compile this document, the final line of text begins "Here are some references to ?? 1.1, ?? 1.2 and theorem 1.3." The attempt to use \crefname to set reference names (I'm not up on the terminology) for the different environments is not working. However, if the line marked with % (*) is uncommented, the second "??" will become "toybox". So it is something to do with being in a \foreach. But I can't find a way to make it work. It is as though the invocation of \crefname is totally ignored within the \foreach. Why does this happen?

\documentclass[a4paper]{amsart}

\def\myTheoremEnvironments{%
    theorem/theorems,%
    cobblestone/cobblestones,%
    toybox/toyboxes%
}

\usepackage{tikz, titlecaps, cleveref}

% Number all definition, theorem, etc. environments using the same counter.
% Start counting again at the start of each section.
% Style these environments using LaTeX's "definition" style.
\theoremstyle{definition}
\newtheorem{baseTheorem}{Base Theorem}[section]
\foreach \x/\y in \myTheoremEnvironments {
    \edef\tmp{\noexpand\newtheorem{\x}[baseTheorem]{\noexpand\titlecap{\x}}}\tmp
    \crefname{\x}{\x}{\y}
}

%\crefname{toybox}{toybox}{toyboxes} % (*)

\begin{document}

\section{Hello, and welcome to my document.}

\begin{cobblestone}
    \label{csref}
    Hello. I am an ``cobblestone'' environment.
\end{cobblestone}

\begin{toybox}
    \label{tbref}
    I am a ``toybox'' environment. What a strange name that is for an environment.
\end{toybox}

\begin{theorem}
    \label{thmref}
    People trying to do strange things with ``foreach'' should expect trouble.
\end{theorem}

Here are some references to \cref{csref}, \cref{tbref} and \cref{thmref}.
I could have tried using just one call to ``cref'' here,
but it's best not to run before you can walk.

\end{document}

Best Answer

The big problem is that \foreach does each cycle inside a group; while \newtheorem definitions are global, \crefname definitions aren't.

I suggest a perhaps more user-friendly interface.

\documentclass[a4paper]{amsart}

\usepackage{titlecaps, cleveref}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\foreachpair}{smm}
 {
  \IfBooleanTF{#1}
   {
    \hammerite_foreachpair:on { #2 } { #3 }
   }
   {
    \hammerite_foreachpair:nn { #2 } { #3 }
   }
 }

\cs_new_protected:Npn \hammerite_foreachpair:nn #1 #2
 {
  \cs_set:Npn \__hammerite_temp:nn ##1 ##2 { #2 }
  \clist_map_inline:nn { #1 } { \__hammerite_dopair:n { ##1 } }
 }
\cs_new:Npn \__hammerite_dopair:n #1
 {
  \__hammerite_dopair:wn #1 \q_stop
 }
\cs_new:Npn \__hammerite_dopair:wn #1/#2 \q_stop
 {
  \__hammerite_temp:nn { #1 } { #2 }
 }
\cs_generate_variant:Nn \hammerite_foreachpair:nn { o }
\ExplSyntaxOff

% Number all definition, theorem, etc. environments using the same counter.
% Start counting again at the start of each section.
% Style these environments using LaTeX's "definition" style.

\theoremstyle{definition}

\newtheorem{baseTheorem}{Base Theorem}[section]

\foreachpair
 {
  theorem/theorems,
  cobblestone/cobblestones,
  toybox/toyboxes,
 }
 {
  \newtheorem{#1}[baseTheorem]{\titlecap{#1}}
  \crefname{#1}{#1}{#2}
 }

\begin{document}

\section{Hello, and welcome to my document.}

\begin{cobblestone}\label{csref}
Hello. I am an ``cobblestone'' environment.
\end{cobblestone}

\begin{toybox}\label{tbref}
I am a ``toybox'' environment. What a strange name that is for an environment.
\end{toybox}

\begin{theorem}\label{thmref}
People trying to do strange things with ``foreach'' should expect trouble.
\end{theorem}

Here are some references to \cref{csref}, \cref{tbref} and \cref{thmref}.
I could have tried using just one call to ``cref'' here,
but it's best not to run before you can walk.

\end{document}

As you see, the second argument of \foreachpair is a comma separated list of items of the form <item-a>/<item-b>; the second argument is like a command definition, at each cycle <item-a> is substituted for #1 and <item-b> is substituted for #2.

There's also an alternative way of calling it

\newcommand\myTheoremEnvironments{
  theorem/theorems,
  cobblestone/cobblestones,
  toybox/toyboxes
}

\foreachpair* \myTheoremEnvironments
 {
  \newtheorem{#1}[baseTheorem]{\titlecap{#1}}
  \crefname{#1}{#1}{#2}
 }

enter image description here

Related Question