The Question
I'd like to write a function which itself takes a list of symbols, e.g.
Foo[{x, y, z}]
to pass to Module
within, but with additional localized symbols, e.g.
Foo[list_, expr_] := Module[Join[list, {a, b, c}], ...]
In other words, I'd like Foo[{x, y, z}]
to behave like
Module[{x, y, z, a, b, c}, ...]
To show what I've tried, though all in vain...
I started without the Join
:
ClearAll[x, y, z, a, b, c, Foo];
x = 7;
Foo[vars_] := Module[vars, x = 43];
Foo[{x, y, z}];
x
which returned, understandably,
Module::lvsym: "Local variable specification {7,y,z} contains 7, which is not a symbol or an assignment to a symbol."
I added SetAttributes
,
ClearAll[x, y, z, a, b, c, Foo];
x = 7;
SetAttributes[Foo, HoldAll];
Foo[vars_] := Module[vars, x = 43];
Foo[{x, y, z}];
x
which returned the desired result, 43
, i.e. unchanged by the assignment within the Module
.
Now, including a Join
,
ClearAll[x, y, z, a, b, c, Foo];
x = 7;
SetAttributes[Foo, HoldAll];
Foo[vars_] := Module[Join[vars, {a, b, c}], x = 43];
Foo[{x, y, z}];
x
I got,
Module::lvlist: "Local variable specification Join[{x,y,z},{a,b,c}] is not a List."
As I understand, the Join
didn't evaluate because Module
itself must be putting a Hold
attribute on (at least, if not more than) its first argument. So, reading that Evaluate
effectively overrides the
HoldFirst
attribute, and causes the first argument to be evaluated,
I tried
ClearAll[x, y, z, a, b, c, Foo];
x = 7;
SetAttributes[Foo, HoldAll];
Foo[vars_] := Module[Evaluate[Join[vars, {a, b, c}]], x = 43];
Foo[{x, y, z}];
x
which returned
Module::lvsym: "Local variable specification {7,y,z,a,b,c} contains 7, which is not a symbol or an assignment to a symbol."
I'm a bit confused as to why x
interpolated, i.e. why the Hold
attribute didn't persist. I guess the Hold
attribute only "carries" the argument, unevaluated, into the function, but doesn't hold the argument beyond? Okay, then should I Hold
the list again?
ClearAll[x, y, z, a, b, c, Foo];
x = 7;
SetAttributes[Foo, HoldAll];
Foo[vars_] := Module[Evaluate[Join[Hold[vars], {a, b, c}]], x = 43];
Foo[{x, y, z}];
x
This resulted in:
Join::heads: Heads Hold and List at positions 1 and 2 are expected to be the same.
Module::lvlist: "Local variable specification Join[Hold[{x,y,z}],{a,b,c}] is not a List."
I made sure to read both Evaluation and Non-Standard Evaluation, but unfortunately these didn't (seem to) help me understand what's happening. I tried Unevaluated
and Defer
just to try, but the results weren't different. I saw a Google Groups thread where the solution seemed to be somehow converting the symbols to strings, then back via Symbol
, but I thought there must be a better way...
Background
@SjoerdCdeVries, in short, I'm trying to write a sort of a While
loop that makes certain convenient symbols (both functions and variables) available to use within (and only within) the looping expression, and also displaying certain results of the loop afterwards. The motivation is sort-of-explained in the "Background" section of Is there anything like a C pointer (or returning a reference) in $Mathematica$?; I'm writing a tool that users (mostly familiar with C
, only) can use to prototype ideas for analyzing time-series data.
I have a more specific use-case example (in code), but I've decided not to share this unless requested—this post is long enough already.
Hold
attribute (see mathematica.stackexchange.com/a/17770/57). $\endgroup$Hold
properly in this case. But perhaps you're saying my answer is in that link; I'll give it a read right now. [...] This is going to take me some time to read (all the related links too) but I believe I'll find my answer. Thanks! $\endgroup$Foo[vars_] := Hold[vars] /. _@{v__} :> Module[{v, a, b, c}, x = 43]
$\endgroup$