I've just recovered from my first real foray into LaTeX3 programming. It was ... an experience. Not a completely unpleasant one and once I'd gotten used to the syntax then I found it a lot easier than it looks. I almost was able to forget that I was programming in a macro language and start thinking in terms of functions and variables again.

Almost.

Where I came up against a brick wall was in the concept of auxiliary functions. I had a main function that did a lot of work. I would quite like to have farmed off parts of it to some other functions, as much for keeping track of what was going on as anything else. But I couldn't work out how to do that separation properly.

Here's an example. Suppose I wanted to compute the Euclidean length of a lot of 2-vectors. In, say, lua then I might write:

function veclen(a,b)
return math.sqrt(a^2 + b^2)
end

Now that isn't directly comparable with a LaTeX3 "function" so let me pretend that I can't write inline formulae in lua and write it out a bit more like LaTeX3.

In the lua function, a and b are local but even if they are not then the commands local s = a and local t = b force them to be local. But the return s breaks out of the function scope and makes the answer available at the next level up. In doing this in TeX, I'd do all my computations inside a group: something like:

Except that that wouldn't work: if I put the \group_end: where I have done so then \l_hobby_veclena_fp has lost its value. If I put it after the assignment then the assignment is local to the group and so is lost moments later.

To cut a long story short: I want the computation to be local so that I can use my temporary variables with impunity, but I need the assignment to be outside the computation group (but not global) so that it can be used by the calling code.

How do I do this? Obviously it is possible because the LaTeX3 functions must do this all the time[1]. TikZ uses "smuggling" to do this: define a temporary global variable to be the answer and then outside the group make the assignment which makes the actual assignment not global but outside the computation group: \global\let\tikz@smuggle=\the@answer\endgroup\let\the@answer=\tikz@smuggle. What's the right LaTeX3-way to do this?

For bonus points, there's another small issue with scoping. In the lua function, the variables a and b were already local: I could reassign them with impunity. In TeX, that's not so easy. If I do something like

\fp_set_eq:NN \l_my_tmpa_fp #1
\fp_set_eq:NN \l_my_tmpb_fp #2

then there's always the danger that I called my function with \calc:NN \l_my_tmpb_fp \l_my_tmpa_fp. How do I make local aliases for my incoming variables without defining a new set of temporary macros for every single function?

[1] I know I could look at the LaTeX3 code - indeed I took a quick glance and got a vague idea, but I suspect that there would be subtleties I'd miss by not asking, and I hope that others will benefit from a more public answer and explanation.

You can use \fp_gset_eq:NN #1 \l_hobby_veclena_fp inside the group.
–
Marco DanielMay 17 '12 at 19:28

1

@MarcoDaniel But that globally sets whatever-#1-was to \l_hobby_veclena_fp. But #1 was (or could have been) only a local variable in the calling function. So it might have another global value that I don't want to touch.
–
Loop SpaceMay 17 '12 at 19:36

Why not use the TikZ way? Seems nice'n'easy to me...
–
clemensMay 17 '12 at 19:42

@cgnieder: I believe in the separation of TikZ and LaTeX3. Seriously, if there's a "proper l3 way" to do this, I'd like to know about it.
–
Loop SpaceMay 17 '12 at 19:46

3

You can use: \edef\@tempa{\endgroup\value=\the\value\relax}\@tempa to move a value (counter, length) out of a group. Don't ask me how to translated that into Klingon, aeh, LaTeX3. The same is possible for macros: \edef\@tempa{\endgroup\def\noexpand\mymacro{\mymacro}}\@tempa or, if you don't want to expand it all the way: \def\@tempa{\endgroup\def\noexpand\mymacro{\unexpanded\expandafter{\mymacro}}}\‌​@tempa, or without e-TeX: \expandafter\endgroup\expandafter\def\expandafter\mymacro\expandafter{\mymacro}‌​. The first two values also work nicely with multiple macros/registers.
–
Martin Scharrer♦May 17 '12 at 20:19

I quite like it but I don't know if should be added to expl3 or not. Suggestions?

The advantage over this approach is that you can use it multiple times within a group to "export" multiple variables, unlike Enrico's answer which is much more simple and efficient for the common case.

Soln 2 (added later)

From some discussion elsewhere on this page with Ahmed, it's become clear to me that the syntax introduced about isn't such a great idea. For example, if you want to set a variable using tl_set:Nx, it's not then appropriate to use that same function to define the variable outside the group. Really what we're looking for is an extension of etextool's \AfterGroup that permits easy expansion control (which is part of the whole design philosophy of expl3).

So separating the local and "group escaped" variables need to be done independently, syntax-wise. Here's a modified implementation of the above that does this. The code looks similar:

Note that since we don't have a mapping between variable types and their setting functions, in this case \int_set:Nn is required twice. This isn't so bad, really, since you could in theory use this for all sorts of setting functions. So the "general" approach here I've written as follows:

So it's easy to see how the former (\group_var_return:NN) is just something like \group_after_insert:nV { \int_set:Nn \y } \y. Whether these levels of abstraction are necessary or helpful is a bit of an open question. I'm inclined to think that at least the \group_var_return:NN one looks good.

@WillRobertson: If this is all about eTeX's \currentgrouptype, please have you simply used the returned number (0,16)? I am sorry, I don't do LaTeX3 at all; I am sure you know that. In the past I have had trouble getting out of deep groups (for error recovery) using \currentgrouptype. I can ask a separate question if that will help.
–
Ahmed MusaMay 18 '12 at 14:44

Will Robertson has sent me an eTeX translation of this LaTeX3 solution, which I have adapted substantially. The difference between Will's and Martin Scharrer's solutions is that Martin's scheme deals with only one type of group (semi simple group). See also etextools's \AfterGroup, which works for all groups but doesn't accumulate code as Will's scheme does.

A disadvantage to this syntax IMO is that what is in \aftergroupexecute still needs to be expanded if you're passing out results of a calculation. E.g., foo must still consist of non-local material.
–
Will RobertsonMay 21 '12 at 6:14

@WillRobertson: I am afraid I don't understand your comment. Perhaps you could send me an email. In the meantime, in the example given, with the *-variant you have the option of executing \def\x{foo} (or any given <code>) within the local group, or simply passing it on to outside the group. In your approach, you hardwired \def#1{#2}, implying that only the definition of #1 could take place within and outside the group.
–
Ahmed MusaMay 21 '12 at 7:24

Let's say you want to take some input and process it using a scratch variable, then output it in \x. So you're writing something like \begingroup \def\@tempa{#1} \processingfunction \y {«something with \@tempa»} \aftergroupexecute{ \def\x{\y} } \endgroup. How do you get \x defined as \y without doing yet more ugly expansion control?
–
Will RobertsonMay 21 '12 at 7:45

To be fair I don't think my syntax is perfect either. I now think it's better to separate local assignment and the declaration of "returning" a variable. So if I re-wrote my answer now I'd prefer a syntax like \group_return_variable:N \x where the local value of \x would remain after the group ended.
–
Will RobertsonMay 22 '12 at 2:11