When we say that “a command is (fully) expandable” we mean that it delivers the intended result by performing no assignment or typesetting instruction (among which there is \relax
).
So your \bbb
works, because its expansion is a length. TeX knows that the first argument to \rule
should contain a (rigid) length, and it performs macro expansion until finding something that fits the requirement.
When TeX sees your \fromlist
, it realizes it's not a length, so it does an expansion and it sees \foreach
. If you do
latexdef -p pgffor -s foreach
from a terminal, you'll get
% pgffor.code.tex, line 60:
\let\foreach=\pgffor@foreach
% pgffor.code.tex, line 45:
\def\pgffor@foreach{%
\pgffor@atbeginforeach%
\let\pgffor@assign@before@code=\pgfutil@empty%
\let\pgffor@assign@after@code=\pgfutil@empty%
\let\pgffor@assign@once@code=\pgfutil@empty%
\let\pgffor@remember@code=\pgfutil@empty%
\let\pgffor@remember@once@code=\pgfutil@empty%
\pgffor@alphabeticsequencefalse%
\pgffor@contextfalse%
%
\let\pgffor@var=\pgfutil@empty
%
\pgffor@vars%
}
and you know that your plan is doomed, because even if \pgffor@atbeginforeach
is expandable (it isn't actually), a bunch of \let
instructions follows and one is already too many.
Is it necessary to run latexdef
to see whether a command is expandable or not? Not really. In general you should not expect a command is expandable, unless somebody tells you it is.
And among the expl3
functions, you can spot those that are (fully) expandable because their name is followed by a star (filled or hollow). The descriptions are found in interface3.pdf
.
Your input is a comma separated list of items, so it fits the syntax of a clist
. And yes, the function \clist_item:nn
has the star after it! So you might do
\ExplSyntaxOn
\cs_set_eq:NN \fromlist \clist_item:nn
\ExplSyntaxOff
but then you'll ask a new question because something like
\def\ccc{1pt,2pt,3pt}
\rule{\fromlist\ccc{2}}{3cm}
doesn't work. How can you accommodate both implicit and explicit comma lists?
My suggestion is to define \fromlist
with a *-variant that accepts an implicit list:
\ExplSyntaxOn
\NewExpandableDocumentCommand{\fromlist}{smm}
{
\IfBooleanTF{#1}
{% there is *
\clist_item:Nn #2 { #3 }
}
{% no *
\clist_item:nn { #2 } { #3 }
}
}
\ExplSyntaxOff
A full example.
\documentclass{article}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\fromlist}{smm}
{
\IfBooleanTF{#1}
{% there is *
\clist_item:Nn #2 { #3 }
}
{% no *
\clist_item:nn { #2 } { #3 }
}
}
\ExplSyntaxOff
\newcommand{\mylist}{1in,2in,3in}
\begin{document}
\rule{\fromlist{1in,2in,3in}{2}}{5pt}
\rule{\fromlist*{\mylist}{3}}{5pt}
\end{document}

\fromlist
is not expandable (for example, try\edef\tmp{\fromlist{{1in,2in,3in}}{2}}
). Thus,\rule
doesn't see the final result for use as its input, and coughs an error.