Why does cleveref's \crefname command have no effect when it appears within a \foreach?

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}

-
The main problem is the usual one: \foreach executes its cycles in a group and \crefname only does local assignments. –  egreg Nov 16 '13 at 15:10
The author of the cleveref package, @TobyCubitt, appears now to be active on this StackExchange. –  Niel de Beaudrap Nov 16 '13 at 20:39
I couldn't remember if there was a good reason for making \crefname do local assignments. Luckily, my former and smarter self kindly left an explanation in the manual (implementation section). To quote myself: "\crefname must not create global definitions, or else it will break babel's \otherlanguage and \foreignlanguage commands." I no longer recall why this will break babel, but presumably the answer's somewhere in the babel manual/code. –  Toby Cubitt Nov 18 '13 at 21:44

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


-

\foreach does each iteration in a group and furthermore \crefname does not expand its second argument.

One can circumvent these problems using \xintForpair from package xinttools:

\documentclass[a4paper]{amsart}

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

% Does not work:
% \def\myTheoremEnvironments{%
%     theorem/theorems,%
%     cobblestone/cobblestones,%
%     toybox/toyboxes%
% }
% \foreach \x/\y in \myTheoremEnvironments {
%     \edef\tmp{\noexpand\newtheorem{\x}[baseTheorem]{\noexpand\titlecap{\x}}}\tmp
%     \crefname{\x}{\x}{\y}
% }

% alternative using \xintForpair:

\usepackage{xinttools}% http://ctan.org/pkg/xint

\xintForpair #1#2 in
{% spaces around commas or like the end of lines here,
% or around parentheses are removed automatically
(theorem,theorems),
(cobblestone,cobblestones),
(toybox,toyboxes)
}
\do
{%  (spaces significant but do not matter)
\newtheorem{#1}[baseTheorem]{\titlecap{#1}}
\crefname{#1}{#1}{#2}
}

\begin{document}\thispagestyle{empty}

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


-
I appreciate the suggestion of the xint loop and I considered using this. It seems like this functionality has only very recently (like, in the last month) been added to xint. The alternative won out in part because I don't have to update my LaTeX installation! –  Hammerite Nov 16 '13 at 21:07
@Hammerite yes it is a recent addition. I completely understand your reasons for going with egreg's answer.B –  jfbu Nov 16 '13 at 22:04

[Y]ou can […] use the .list handler that uses \foreach internally but

• doesn’t group its content […] and
• can be used with the already expanded content as #1 (rather than as a macro sequence).

Using

\pgfkeys{/utils/my Foreach/.code args={#1/#2}{\newtheorem{#1}[baseTheorem]{\titlecap{#1}}%
\crefname{#1}{#1}{#2}},
/utils/my Foreach/.list/.expand once=\myTheoremEnvironments}


gives

-