1

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.

Andrew Cheong
  • 3,576
  • 17
  • 42
  • Where would you use this for? – Sjoerd C. de Vries Oct 27 '13 at 10:02
  • @SjoerdC.deVries - Added some background. Initially I included a use-case, but perhaps the concise summary is enough. Let me know if not. – Andrew Cheong Oct 27 '13 at 10:26
  • 1
    If you want to do a pass-by-reference you could use the Hold attribute (see http://mathematica.stackexchange.com/a/17770/57). – Sjoerd C. de Vries Oct 27 '13 at 11:08
  • @SjoerdC.deVries - Well, that's where I'm stuck; I don't seem to know how to use 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! – Andrew Cheong Oct 27 '13 at 11:12
  • @SjoerdC.deVries - The whole code-injection thing was eye-opening. LeonidShifrin uses a new symbol x1 to localize an existing one x; maybe it's not possible to do this using the same symbol (which would explain my futile efforts). One of the comments in that question led to this thread, which was a slightly different problem in that it began with string names of symbols, not symbols, but OleksandrR. presents a function referenceTo that can handle both – Andrew Cheong Oct 29 '13 at 09:36
  • string names of symbols as well as symbols themselves. Unfortunately, my several attempts to copy and use his function failed, and though I've tried, I'm not yet able to understand his code (I didn't even know what /: was before beginning to inspect his code) enough to debug what's wrong. For now, I've given up, and gone after the trivial solution: using a Module inside another Module, since I am able to pass symbols along as unmodified lists... I just can't seem to modify the function arguments. – Andrew Cheong Oct 29 '13 at 09:40
  • 1
    I'm not really sure what you're trying to do, but does this work for you? Foo[vars_] := Hold[vars] /. _@{v__} :> Module[{v, a, b, c}, x = 43] – Simon Woods Oct 29 '13 at 11:07
  • @SimonWoods - Wow, that's it! If I weren't so new to this idea of pattern replacement, maybe I could have thought that up (or maybe not). I'm still stuck on a C++ way of thinking. Would you post this as an answer, please? – Andrew Cheong Oct 29 '13 at 11:12

1 Answers1

6

You can "inject" your variables into the module like this:

ClearAll[x, y, z, a, b, c, Foo];
x = 7;
SetAttributes[Foo, HoldAll];
Foo[vars_] := Hold[vars] /. _@{v__} :> Module[{v, a, b, c}, x = 43]

Foo[{x, y, z}]
(* 43 *)

x
(* 7 *)

For more information have a look at some of the questions and answers in these search results: https://mathematica.stackexchange.com/search?q=injector

Simon Woods
  • 84,945
  • 8
  • 175
  • 324